目录
数据交互浅析:
内容协商:
数据交互浅析:
众所周知,前后端分离项目的前端我们是利用Jquery实现ajax异步请求与后端进行数据交互:意思就是从前端页面中取出数据,请求到对应的controller层方法上,然后后端对前端请求的数据进行业务逻辑处理,最后将对应的状态返回给前端;
HTTPMessageConverter原理:
将你项目中的实体类数据转为JSON数据或者倒转,利用读写方法;
设计restful风格的API,通过json数据进行前后端交互:数据如何解析的?SpringMVC中启动时会自动配置一些HttpMessageConverter,那么什么作用呢?能够支持json数据类型,当后端接收到请求时会判断是否能读,能读则读,返回结果时判断是否能写,能写则写;
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> var1, @Nullable MediaType var2);
boolean canWrite(Class<?> var1, @Nullable MediaType var2);
List<MediaType> getSupportedMediaTypes();
T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}
1、那么问题来了读归读写归写,但是你的数据得转换,那么转换数据是不是得需要Converter,那么在WebMvcConfigurationSupport类中,里面有一个非常关键的方法:addDefaultHttpMessageConverters();
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper));
}
else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
}
else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}
由上述可以知道这是用来判断的,找到数据需要的Converter转换器,如果需要的话,就添加到messageConverter中;这里需要注意当我们配置了自己的MessageConverter时(专门储存Converter的地方),addDefaultHttpMessageConverters()方法就不会被SpringMVC调用;
2、getMessageConverter():用来判断converters转换器是用自定义的还是springMVC默认配置的(这里有点迷,isEmpty是判断转换器中的内容吗?):我的理解是:如果转换器为空(意思就是没东西取了),我们就new一个集合(转换器的),然后配置当前的转换器,(这里应该是我们某个数据需要某个转换器时,如果这个转换器没有,就new一个转换器集合,并配置一个对应这种数据的转换器,将数据转换)——也就是自己的转换器;然后后面判断转换器中内容是否为空,若为空,springMVC就会加载addDefaultHttpMessageConverters(),去获取属性对应的转换器;
protected final List<HttpMessageConverter<?>> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
3、对于MessageConverters的处理:
遍历的每一个converter去比较,直至找到Jackson的处理类型;
// 遍历 messageConverters
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
// 上文类关系图处要重点记住的地方,主要判断 MappingJackson2HttpMessageConverter 是否是 GenericHttpMessageConverter 类型
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = genericConverter.read(targetType, contextClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
else if (targetClass != null) {
if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Read [" + targetType + "] as \"" + contentType + "\" with [" + converter + "]");
}
if (inputMessage.getBody() != null) {
inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
}
break;
}
}
}
4、然后判断读取操作:能读就读,
protected Object _readMapAndClose(JsonParser p0, JavaType valueType)
throws IOException
{
try (JsonParser p = p0) {
Object result;
JsonToken t = _initForReading(p);
if (t == JsonToken.VALUE_NULL) {
// Ask JsonDeserializer what 'null value' to use:
DeserializationContext ctxt = createDeserializationContext(p,
getDeserializationConfig());
result = _findRootDeserializer(ctxt, valueType).getNullValue(ctxt);
} else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
result = null;
} else {
DeserializationConfig cfg = getDeserializationConfig();
DeserializationContext ctxt = createDeserializationContext(p, cfg);
JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
if (cfg.useRootWrapping()) {
result = _unwrapAndDeserialize(p, ctxt, cfg, valueType, deser);
} else {
result = deser.deserialize(p, ctxt);
}
ctxt.checkUnresolvedObjectId();
}
// Need to consume the token too
p.clearCurrentToken();
return result;
}
}
此时,解析已经全部完成,那么还有后端的结果(也就是返回给前端的状态)的过程没有完成;
内容协商:
首先,什么是内容协商?
其实就和之前讲前后端数据交互一样的,这里往上面的接;针对标注了@ResponseBody注解处理的方法,我们的服务器会根据客户端的需要来返回不同格式的数据,比如json、xml...
原理:
1.当浏览器发送请求时,请求头(Request Headers)会携带accept信息:(eg:img/webp,img/*等等)
2.然后springboot就会对controller下的方法进行返回数据类型的处理,那么问题来了,怎么样处理的呢?上面就已经说过了,是通过messageConverter消息转换器进行处理的,messageConverter有默认的所有converter,springboot启动时它就会被加载,从getMessageConveter()可以知道优先自定义的messageConverter,如果有的话就进行读写操作;然后数据类型转换之后,springboot会统计所有转换类型格式;
?
3.上述两步可以理解我们获取了两种类型,一种客户端的,一种服务器响应的,然后我们进行嵌套遍历一下,就可以得到二者兼容的类型;
?
总的来说:
内容协商就是客户端发送请求我们要哪种格式的数据,同时服务器对自己本身多种响应的格式进行兼容处理,然后根据要求返回对应格式;
|