目录
数据交互浅析:
内容协商:
众所周知,前后端分离项目的前端我们是利用Jquery实现ajax异步请求与后端进行数据交互:意思就是从前端页面中取出数据,请求到对应的controller层方法上,然后后端对前端请求的数据进行业务逻辑处理,最后将对应的状态返回给前端;
HTTPMessageConverter原理:
将你项目中的实体类数据转为JSON数据或者倒转,利用读写方法;
设计restful风格的API,通过json数据进行前后端交互:数据如何解析的?SpringMVC中启动时会自动配置一些HttpMessageConverter,那么什么作用呢?能够支持json数据类型,当后端接收到请求时会判断是否能读,能读则读,返回结果时判断是否能写,能写则写;
public interface HttpMessageConverter {
boolean canRead(Class> var1, @Nullable MediaType var2);
boolean canWrite(Class> var1, @Nullable MediaType var2);
List 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
> messageConverters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setWriteAcceptCharset(false); messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(stringConverter); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new SourceHttpMessageConverter 由上述可以知道这是用来判断的,找到数据需要的Converter转换器,如果需要的话,就添加到messageConverter中;这里需要注意当我们配置了自己的MessageConverter时(专门储存Converter的地方),addDefaultHttpMessageConverters()方法就不会被SpringMVC调用;
2、getMessageConverter():用来判断converters转换器是用自定义的还是springMVC默认配置的(这里有点迷,isEmpty是判断转换器中的内容吗?):我的理解是:如果转换器为空(意思就是没东西取了),我们就new一个集合(转换器的),然后配置当前的转换器,(这里应该是我们某个数据需要某个转换器时,如果这个转换器没有,就new一个转换器集合,并配置一个对应这种数据的转换器,将数据转换)——也就是自己的转换器;然后后面判断转换器中内容是否为空,若为空,springMVC就会加载addDefaultHttpMessageConverters(),去获取属性对应的转换器;
protected final List
> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList >(); 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
> converterType = (Class >) 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 ) 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
此时,解析已经全部完成,那么还有后端的结果(也就是返回给前端的状态)的过程没有完成;
首先,什么是内容协商?
其实就和之前讲前后端数据交互一样的,这里往上面的接;针对标注了@ResponseBody注解处理的方法,我们的服务器会根据客户端的需要来返回不同格式的数据,比如json、xml...
原理:
1.当浏览器发送请求时,请求头(Request Headers)会携带accept信息:(eg:img/webp,img/*等等)
2.然后springboot就会对controller下的方法进行返回数据类型的处理,那么问题来了,怎么样处理的呢?上面就已经说过了,是通过messageConverter消息转换器进行处理的,messageConverter有默认的所有converter,springboot启动时它就会被加载,从getMessageConveter()可以知道优先自定义的messageConverter,如果有的话就进行读写操作;然后数据类型转换之后,springboot会统计所有转换类型格式;
3.上述两步可以理解我们获取了两种类型,一种客户端的,一种服务器响应的,然后我们进行嵌套遍历一下,就可以得到二者兼容的类型;
总的来说:
内容协商就是客户端发送请求我们要哪种格式的数据,同时服务器对自己本身多种响应的格式进行兼容处理,然后根据要求返回对应格式;