不要問我閱讀spring源碼有什麼用,問就是沒有用,只是讓我本身使用spring的過程當中自信點!java
spring-相關文章web
見名知意,上面的兩個消息處理器是分別針對json,xmlspring
下面代碼有部分若有不一樣請看 spring-mcv執行流程express
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//執行請求 returnValue 爲響應值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
//在這個地方去作的 消息轉換 spring 響應結果用的是輸出流
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
複製代碼
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
//獲取流
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
這裏
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
複製代碼
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
//獲取響應數據類型
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
................................
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
//獲取客戶端能夠接受的 媒體類型
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
//獲取服務端能夠處理的媒體類型 (重點)
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
//肯定最後使用的媒體類型,debug 你會發現 只要存在 xml 的媒體類型,則使用xml,沒有的話纔會考慮json,他們直接存在一個順序的問題
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using '" + selectedMediaType + "', given " +
acceptableTypes + " and supported " + producibleTypes);
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
//處理消息
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
// 使用輸出流 寫出結果
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
複製代碼
重點看下怎麼獲取服務端支持的媒體類型json
//關鍵就是這句代碼
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
複製代碼
本身總結的三種狀況spring-mvc
protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
//假如使用@RequestMapping指定了 mediaTypes 則存在值 下面會講
Set<MediaType> mediaTypes =
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (!CollectionUtils.isEmpty(mediaTypes)) {
return new ArrayList<>(mediaTypes);
}
else if (!this.allSupportedMediaTypes.isEmpty()) {
//假如沒指定
List<MediaType> result = new ArrayList<>();
//this.messageConverters沒指定全局的話爲 7種 包含 MappingJackson2HttpMessageConverter Jaxb2RootElementHttpMessageConverter
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
else if (converter.canWrite(valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
//假如響應的類型存在@XmlRootElement 返回的結果爲:
//1.application/xml
//2.text/xml
//3.application/*+xml
//4.application/json
//5.application/*+json
//注意存在一個順序問題,假如存在xml xml必在前面
//假如響應的類型不存在@XmlRootElement 返回的結果爲:
//1.application/json
//2.application/*+json
//總結下的意思就是 : 返回值類型是被@XmlRootElement 註釋的的話 當作xml處理的 反之爲json,在根據這個返回獲取messageConverter 使用的獲取添加爲converter.canWrite(valueClass, null)
return result;
}
else {
return Collections.singletonList(MediaType.ALL);
}
}
複製代碼
@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
//使用了@RequestMapping 指定的話 直接返回mediaTypes ,因此說咱們就看下mediaTypes的值是從哪裏取到的就能夠了
Set<MediaType> mediaTypes =
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
............................................................................
}
複製代碼
@Override
public Object getAttribute(String name) {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
//會發現是從request.attribute 中那的值,attribute就是一個map
//org.springframework.web.servlet.HandlerMapping.producibleMediaTypes 爲key
//如今就去看看attribute是在何時存在的這個鍵值對
return request.getAttribute(name);
}
複製代碼
Attribute 存放媒體類型的鍵值對是在 handlerMethod 的過程當中從 RequestMappingInfo 中獲取到的,假如對spring如何獲取 handlerMethod 不瞭解的話能夠看下 spring-mvc-handlerMapping 是怎麼存放咱們的請求路徑的-源碼mvc
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 這個地方 去獲取的 媒體類型
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
複製代碼
@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
super.handleMatch(info, lookupPath, request);
................................................................................
if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
//獲取到指定的媒體類型
Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
//放入 Attribute key 爲 org.springframework.web.servlet.HandlerMapping.producibleMediaTypes
request.set Attribute (PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
}
}
複製代碼
能夠看出媒體類型是在 RequestMappingInfo.getProducesCondition().getProducibleMediaTypes 中獲取的app
而後前面幾篇文章都講過 RequestMappingInfo 是在 requestMappingHandlerMapping 初始化的過程當中出現的ide
假若有能夠看下前面的幾篇文源碼分析
//這段代碼是 獲取 handlerMethod 中的一個片斷
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//建立RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
複製代碼
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//獲取 一些值 包括 媒體類型
//這個方法是獲取@RequestMapping 註解的值,咱們其中就包含了咱們今天講的 返回值類型 xml ? json
//裏面東西挺多的不看了,只要知道是獲取註解值的就好了
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
//建立 RequestMappingInfo 點進去會返現 媒體類型的值是從requestMapping中取的
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
複製代碼
先看兩段代碼,在上面都出現過的代碼
@SuppressWarnings("unchecked")
protected List<MediaType> getProducibleMediaTypes( HttpServletRequest request, Class<?> valueClass, @Nullable Type targetType) {
//處理第一種請款的,上面講過了
Set<MediaType> mediaTypes =
(Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (!CollectionUtils.isEmpty(mediaTypes)) {
return new ArrayList<>(mediaTypes);
}
//處理第二種第三種狀況的,是這個
//首先有兩個概念
//1.MediaType 媒體類型(這是我直譯夠來的,也不知作別人怎麼稱呼的)
//2.HttpMessageConverter 消息轉換器
//而後
//在沒有定義全局指定的狀況下 messageConverters 爲 8種
//假如重寫了org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.configureMessageConverters的狀況下 messageConverters的值爲你本身添加的
//咱們最後是要拿到合適的消息處理器(messageConverter)
//閱讀writeWithMessageConverters方法咱們會發現咱們要獲取的消息處理器是根據媒體類型判斷的
//根據媒體類型去 messageConverters 取消息處理器,你想下假如你只配置了一個json的,無論媒體類型有多少是否是隻能爲json'的處理器
//因此說總結下,能夠這麼說:
//1.假如沒有配置全局的(messageConverters裏面有8種) 是根據媒體類型
//2.假如配置了全局的,是根據你配置的和媒體類型,假如最後找不到會報錯
else if (!this.allSupportedMediaTypes.isEmpty()) {
List<MediaType> result = new ArrayList<>();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter && targetType != null) {
if (((GenericHttpMessageConverter<?>) converter).canWrite(targetType, valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
else if (converter.canWrite(valueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
return result;
}
else {
return Collections.singletonList(MediaType.ALL);
}
}
複製代碼
@SuppressWarnings({"rawtypes", "unchecked"})
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
....................................................
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
//客戶端直接在請求頭中定義聲明瞭使用哪一個返回類型,那就直接使用
if (contentType != null && contentType.isConcrete()) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
//獲取咱們服務端支持的媒體類型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
return;
}
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
//循環 getProducibleMediaTypes 中獲取的媒體類型 順序 假若有xml 就必定使用xml
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
//這裏也是使用canWrite進行判斷是否合適
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
...............................................
return;
}
}
}
}
複製代碼
我感受上面的代碼我應該講的比較清晰了
如今去看下messageConverters的值是在何時被賦值的
RequestMappingHandlerAdapter初始化的過程完成的messageConverters的賦值
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
//這裏
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
//這裏
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
.........................................................
return resolvers;
}
複製代碼
在afterPropertiesSet只是一些傳遞值的過程,仍是沒有找到數據源,那咱們去看看RequestMappingHandlerAdapter實例化的過程
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
//建立 RequestMappingHandlerAdapter 這個時候其實使用四個默認的 消息轉換器的
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(mvcContentNegotiationManager());
//這裏很關鍵
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
...........................................................................
return adapter;
}
複製代碼
protected final List<HttpMessageConverter<?>> 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;
}
複製代碼