詳解SpringBoot中添加@ResponseBody注解會發(fā)生什么
SpringBoot版本2.2.4.RELEASE。
【1】SpringBoot接收到請求
① springboot接收到一個請求返回json格式的列表,方法參數(shù)為JSONObject 格式,使用了注解@RequestBody
為什么這里要說明返回格式、方法參數(shù)、參數(shù)注解?因為方法參數(shù)與參數(shù)注解會影響你使用不同的參數(shù)解析器與后置處理器!通常使用WebDataBinder進(jìn)行參數(shù)數(shù)據(jù)綁定結(jié)果也不同。
將要調(diào)用的目標(biāo)方法如下:
@ApiOperation(value="分頁查詢")
@RequestMapping(value = "/listPage",method = RequestMethod.POST)
@ResponseBody
public ResponseBean listPage(@RequestBody JSONObject params){
Integer pageNum = params.getInteger("pageNum");
Integer pageSize = params.getInteger("pageSize");
String vagueParam = params.getString("vagueParam");
IPage<TbSysGoodsCategory> indexPage = new Page<>(pageNum, pageSize);
QueryWrapper<TbSysGoodsCategory> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(vagueParam)){
queryWrapper.like("name",vagueParam).or().like("code",vagueParam);
}
//排序
queryWrapper.orderByDesc("id");
indexPage = tbSysGoodsCategoryService.page(indexPage,queryWrapper);
return new ResponseBean<>(true, indexPage, CommonEnum.SUCCESS_OPTION);
}
如下所示,首先進(jìn)入DispatcherServlet使用RequestMappingHandlerAdapter進(jìn)行處理。

而RequestMappingHandlerAdapter (extends AbstractHandlerMethodAdapter)會調(diào)用父類AbstractHandlerMethodAdapter的handle方法進(jìn)行處理。
AbstractHandlerMethodAdapter.handle方法源碼如下:
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}

可以看到RequestMappingHandlerAdapter還實(shí)現(xiàn)了InitializingBean接口,該接口只有一個抽象方法afterPropertiesSet用于在BeanFactory設(shè)置完bean屬性后執(zhí)行,具體可參考博文:Spring - bean的初始化和銷毀幾種實(shí)現(xiàn)方式詳解
② RequestMappingHandlerAdapter.handleInternal
這里首先在this.checkRequest(request)對請求進(jìn)行了檢測,HttpRequestMethodNotSupportedException異常就是這里拋出的。
//1.檢測請求方法是否支持;
//2.檢測是否需要session但是沒有獲取到
protected final void checkRequest(HttpServletRequest request) throws ServletException {
// Check whether we should support the request method.
String method = request.getMethod();
if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
}
// Check whether a session is required.
if (this.requireSession && request.getSession(false) == null) {
throw new HttpSessionRequiredException("Pre-existing session required but none found");
}
}
其他沒有什么需要特殊說明的,然后直接調(diào)用了invokeHandlerMethod方法進(jìn)行實(shí)際業(yè)務(wù)處理。

【2】RequestMappingHandlerAdapter.invokeHandlerMethod核心處理
RequestMappingHandlerAdapter.invokeHandlerMethod
這個方法十分重要,是請求處理流程中的核心方法。這個方法會根據(jù)handlerMethod獲取一個ServletInvocableHandlerMethod 并對其進(jìn)行各種屬性設(shè)置然后調(diào)用其invokeAndHandle方法進(jìn)行處理。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 對應(yīng) 2
ServletWebRequest webRequest = new ServletWebRequest(request, response);
Object result;
try {
// 對應(yīng) 3
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
// 對應(yīng) 4
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
// 對應(yīng) 5
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 對應(yīng) 6
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//對應(yīng) 7
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(this.logger, (traceOn) -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//這里會跳到【3】ServletInvocableHandlerMethod.invokeAndHandle
invocableMethod.invokeAndHandle(webRequest, mavContainer, new Object[0]);
if (!asyncManager.isConcurrentHandlingStarted()) {
//這里會跳到【4】RequestMappingHandlerAdapter.getModelAndView
ModelAndView var15 = this.getModelAndView(mavContainer, modelFactory, webRequest);
return var15;
}
result = null;
} finally {
//這里會跳到【5】ServletWebRequest.requestCompleted
webRequest.requestCompleted();
}
return (ModelAndView)result;
}
① 此時的handlerMethod是什么?
如下圖所示,handlermethod里面有bean、創(chuàng)建bean的工廠、bean的類型、原始方法method、橋接方法bridgedMethod以及參數(shù)對象parameters等關(guān)鍵屬性。
其他都容易理解,那么什么是bridgedMethod?(后續(xù)單獨(dú)分析)

② 此時的ServletWebRequest webRequest是什么?
這個倒是很簡單,如下圖所示:

③ 此時的WebDataBinderFactory binderFactory
WebDataBinderFactory binderFactory = this.getDataBinderFactory(handlerMethod);
RequestMappingHandlerAdapter.getDataBinderFactory源碼如下:
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
//獲取handlerType
Class<?> handlerType = handlerMethod.getBeanType();
//根據(jù)handlerType 從initBinderCache獲取到@InitBinder注解的方法
Set<Method> methods = this.initBinderCache.get(handlerType);
//如果initBinderCache中沒有,就從handlerType查找@InitBinder注解的方法
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
//遍歷controllerAdviceBean的方法列表,從適合handlerType中拿到其方法列表
//然后封裝為一個個InvocableHandlerMethod放到initBinderMethods中
// Global methods first
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
}
首先Class<?> handlerType = handlerMethod.getBeanType();通過handlerMethod獲取到handlerTYPE,handlerTYPE聲明了當(dāng)前完整類路徑以及類上面的注解。其值如下:

然后Set<Method> methods = this.initBinderCache.get(handlerType);嘗試先從initBinderCache這個ConcurrentHashMap中獲取當(dāng)前類的使用了InitBinder注解的方法列表。如果methods為空,則從handlerType中獲取使用了@InitBinder注解的方法,然后放到initBinderCache中,對應(yīng)代碼this.initBinderCache.put(handlerType, methods);

這個很關(guān)鍵。SpringBoot請求處理流程中最重要的一步就是數(shù)據(jù)綁定,即將參數(shù)寫到目標(biāo)對象上面。那么這里就涉及到參數(shù)校驗、數(shù)據(jù)格式轉(zhuǎn)換、綁定結(jié)果對象、錯誤對象等。

最后return createDataBinderFactory(initBinderMethods);其會拿到WebBindingInitializer創(chuàng)建數(shù)據(jù)綁定工廠,。
protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods)
throws Exception {
return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer());
}
DataBinderFactory其屬性ConfigurableWebBindingInitializer對象提供了基礎(chǔ)功能,該對象中WebConversionService中轉(zhuǎn)換器實(shí)例如下:

④ 根據(jù)handlerMethod和binderFactory獲取到ModelFactory modelFactory
ModelFactory modelFactory = this.getModelFactory(handlerMethod, binderFactory);
RequestMappingHandlerAdapter.getModelFactory方法源碼如下:
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//獲取當(dāng)前handlerMethod對應(yīng)的handlerType的SessionAttributesHandler
//--如果沒有就創(chuàng)建一個new SessionAttributesHandler(handlerType, this.sessionAttributeStore)
//參考④-①
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//獲取handlerType
Class<?> handlerType = handlerMethod.getBeanType();
//獲取添加了@ModelAttribute注解的方法
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
//從controllerAdviceBean中獲取適合當(dāng)前handlerType的method,
//并封裝為一個個InvocableHandlerMethod然后添加到attrMethods
// Global methods first
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//遍歷methods并封裝為一個個InvocableHandlerMethod然后添加到attrMethods
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
//根據(jù)attrMethods, binderFactory, sessionAttrHandler創(chuàng)建一個ModelFactory對象
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
可以看到modelFactory主要屬性modelMethods、dataBindFactory和sessionAttributeHandler都是為參數(shù)綁定數(shù)據(jù)服務(wù)的。

④-① RequestMappingHandlerAdapter.getSessionAttributesHandler獲取給定類型的SessionAttributesHandler
方法源碼如下:
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
//獲取當(dāng)前handlerMethod對應(yīng)的handlerType的SessionAttributesHandler
//--如果沒有就創(chuàng)建一個new SessionAttributesHandler(handlerType, this.sessionAttributeStore)
//參考④-①
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
//獲取handlerType
Class<?> handlerType = handlerMethod.getBeanType();
//獲取添加了@ModelAttribute注解的方法
Set<Method> methods = this.modelAttributeCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
this.modelAttributeCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> attrMethods = new ArrayList<>();
//從controllerAdviceBean中獲取適合當(dāng)前handlerType的method,
//并封裝為一個個InvocableHandlerMethod然后添加到attrMethods
// Global methods first
this.modelAttributeAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//遍歷methods并封裝為一個個InvocableHandlerMethod然后添加到attrMethods
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
//根據(jù)attrMethods, binderFactory, sessionAttrHandler創(chuàng)建一個ModelFactory對象
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
④-①-①SessionAttributesHandler.SessionAttributesHandler
構(gòu)造方法源碼如下:
public SessionAttributesHandler(Class<?> handlerType, SessionAttributeStore sessionAttributeStore) {
Assert.notNull(sessionAttributeStore, "SessionAttributeStore may not be null");
this.sessionAttributeStore = sessionAttributeStore;
//嘗試從handlerType獲取@SessionAttributes注解
SessionAttributes ann = AnnotatedElementUtils.findMergedAnnotation(handlerType, SessionAttributes.class);
if (ann != null) {
//注解的name屬性值放入attributeNames中
Collections.addAll(this.attributeNames, ann.names());
//注解的type屬性值放入 attributeTypes
Collections.addAll(this.attributeTypes, ann.types());
}
//把所有的attributeNames放入knownAttributeNames
//在初始化model方法initModel將會使用這些數(shù)據(jù)
this.knownAttributeNames.addAll(this.attributeNames);
}
也就是經(jīng)過③④兩步,創(chuàng)建binderFactory、modelFactory后就會拿到匹配當(dāng)前handlerMethod的那些@InitBinder、@ModelAttribute的方法(HandlerMethod對象)以及SessionAttributesHandler !這三個東西能做什么?當(dāng)你為目標(biāo)方法參數(shù)綁定數(shù)據(jù)的時候就會用到!
⑤ 創(chuàng)建核心處理對象ServletInvocableHandlerMethod invocableMethod并為其屬性賦值
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ServletInvocableHandlerMethod invocableMethod = this.createInvocableHandlerMethod(handlerMethod);創(chuàng)建ServletInvocableHandlerMethod實(shí)例:
protected HandlerMethod(HandlerMethod handlerMethod) {
Assert.notNull(handlerMethod, "HandlerMethod is required");
this.bean = handlerMethod.bean;
this.beanFactory = handlerMethod.beanFactory;
this.beanType = handlerMethod.beanType;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters;
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.description = handlerMethod.description;
this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
}
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);為invocableMethod設(shè)置參數(shù)解析器組合對象-HandlerMethodArgumentResolverComposite。其有List<HandlerMethodArgumentResolver> argumentResolvers和Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache兩個重要屬性。其中具體解析器值列表如下:

我想你現(xiàn)在應(yīng)該知道為什么方法參數(shù)使用@RequestBody就可以進(jìn)行參數(shù)綁定了吧!
繼續(xù)看returnValueHandlers,也就是返回結(jié)果處理器。其中returnValueHandlers是HandlerMethodReturnValueHandlerComposite實(shí)例,就像HandlermethodArgumentResolverComposite一樣,它包含了所有HandlerMethodReturnValueHandler的列表,并在Spring啟動時完成注冊。其值列表如下:

ok,我們的主題來了。就是這個RequestResponseBodyMethodProcessor后置處理器對@ResponseBody注解進(jìn)行的處理!
繼續(xù)往下走,invocableMethod.setDataBinderFactory(binderFactory);給invocableMethod設(shè)置了DataBinderFactory。這個同上都是為數(shù)據(jù)參數(shù)綁定服務(wù),繼續(xù)往下看invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);parameterNameDiscoverer這里值列表如下:

⑥ 創(chuàng)建mavContainer進(jìn)行數(shù)據(jù)的初步處理
//創(chuàng)建ModelAndViewContainer 實(shí)例對象 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //從請求中獲取InputFlashMap并把其數(shù)據(jù)放入defaultModel中,flashmap的作用是在redirect中傳遞參數(shù) mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); //調(diào)用modelFactory對model進(jìn)行初始化 modelFactory.initModel(webRequest, mavContainer, invocableMethod); //重定向時忽略默認(rèn)Model mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
首先看下ModelAndViewContainer,其核心有三個屬性view-視圖對象,defaultModel-默認(rèn)的數(shù)據(jù)存放地方以及redirectModel-重定向時數(shù)據(jù)存放地方。

modelFactory.initModel(webRequest, mavContainer, invocableMethod);,這里對model做了處理。也可以說是對目標(biāo)方法實(shí)際調(diào)用前對數(shù)據(jù)做的最后一次處理:
public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod)
throws Exception {
//獲取會話屬性鍵值對
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
//對model中屬性-值進(jìn)行合并處理:稱之為補(bǔ)缺更合適
//如果model中不存在,則放入---這個很重要
container.mergeAttributes(sessionAttributes);
//調(diào)用標(biāo)注了@ModelAttribute注解的方法
invokeModelAttributeMethods(request, container);
//如果handlerMethod的方法參數(shù)標(biāo)注了@ModelAttribute注解并且在sessionAttributetes存在/或類型匹配,則對其進(jìn)行遍歷
//嘗試獲取值,如果獲取不到值就會拋出異常;如果獲取到值就會放到model-defaultModel中
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
}
container.addAttribute(name, value);
}
}
}
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);獲取會話屬性鍵值對,方法源碼如下:
public Map<String, Object> retrieveAttributes(WebRequest request) {
Map<String, Object> attributes = new HashMap<>();
//遍歷通過@SessionAttributes注解獲取的name
for (String name : this.knownAttributeNames) {
//從session中獲取name對應(yīng)的值
Object value = this.sessionAttributeStore.retrieveAttribute(request, name);
if (value != null) {
//如果值存在,則放入attributes
attributes.put(name, value);
}
}
return attributes;
}
container.mergeAttributes(sessionAttributes);關(guān)于ModelMap.mergeAttributes合并屬性方法源碼如下:
#也就是說遍歷sessionAttributes ,如果model中不存在,就放入。如果存在,就跳過!注意,不會進(jìn)行值覆蓋
public ModelMap mergeAttributes(@Nullable Map<String, ?> attributes) {
if (attributes != null) {
attributes.forEach((key, value) -> {
if (!this.containsKey(key)) {
this.put(key, value);//this這里值的是modelMap,也就是defaultModel
}
});
}
return this;
}
這里 invokeModelAttributeMethods(request, container);調(diào)用了@ModelAttribute注解的方法,該方法通常會對model中的值進(jìn)行更新。從另外一個方面來說呢,類里面的@ModelAttribute方法會在目標(biāo)方法調(diào)用前逐個進(jìn)行調(diào)用!,方法源碼如下:
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
//循環(huán)調(diào)用modelMethod
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
ModelAttribute ann = modelMethod.getMethodAnnotation(ModelAttribute.class);
Assert.state(ann != null, "No ModelAttribute annotation");
if (container.containsAttribute(ann.name())) {
if (!ann.binding()) {
container.setBindingDisabled(ann.name());
}
continue;
}
//反射調(diào)用方法并獲取返回值
Object returnValue = modelMethod.invokeForRequest(request, container);
//如果返回值不為空,就放入model-(returnValueName, returnValue)
if (!modelMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!ann.binding()) {
container.setBindingDisabled(returnValueName);
}
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
關(guān)于findSessionAttributeArguments方法源碼如下:
//從方法參數(shù)中找到在(@SessionAttributes注解的屬性/參數(shù))中存在的或者類型匹配
// 且方法參數(shù)上標(biāo)注了@ModelAttribute注解的屬性名集合
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
List<String> result = new ArrayList<>();
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(parameter);
Class<?> paramType = parameter.getParameterType();
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
result.add(name);
}
}
}
return result;
}
⑦ 異步請求
這一塊先不用管,后續(xù)分析
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
接下來調(diào)用invocableMethod.invokeAndHandle(webRequest, mavContainer);就到了ServletInvocableHandlerMethod.invokeAndHandle。
【3】調(diào)用目標(biāo)方法并對返回值進(jìn)行處理ServletInvocableHandlerMethod.invokeAndHandle
其類繼承示意圖如下:

ServletInvocableHandlerMethod.invokeAndHandle方法源碼如下:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//調(diào)用目標(biāo)方法并獲取返回值,這里對應(yīng) 【3.1】 InvocableHandlerMethod.invokeForRequest調(diào)用目標(biāo)方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//設(shè)置響應(yīng)狀態(tài)
setResponseStatus(webRequest);
//如果返回值為null,則將mavContainer.RequestHandled設(shè)置為true,表示已經(jīng)處理不需要視圖解析
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
//將mavContainer.RequestHandled設(shè)置為false
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
//返回值進(jìn)行處理 ,這里對應(yīng)【3.2】
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
關(guān)于mavContainer.setRequestHandled(false);源碼如下:
public void setRequestHandled(boolean requestHandled) {
this.requestHandled = requestHandled;
}
檢驗請求添加了@ResponseBody注解的方法是否已經(jīng)處理完,如果處理完則視圖解析不再需要。當(dāng)方法參數(shù)有ServletResponse或者OutputStream類型時,同樣可以設(shè)置這個標(biāo)識。requestHandled 默認(rèn)值為false。
【3.1】 InvocableHandlerMethod.invokeForRequest調(diào)用目標(biāo)方法
其方法源碼如下所示,結(jié)構(gòu)很清晰:獲取方法參數(shù)值然后調(diào)用目標(biāo)方法:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//解析參數(shù)--這里對應(yīng) 1
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//根據(jù)上面得到的參數(shù)值調(diào)用目標(biāo)方法 這里對應(yīng) 2
return doInvoke(args);
}
① 解析參數(shù)getMethodArgumentValues
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//獲取到方法的參數(shù)對象 MethodParameter[]數(shù)組
MethodParameter[] parameters = getMethodParameters();
//如果為空,返回空參數(shù)組
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
//遍歷MethodParameter[] parameters,對每一個方法參數(shù)對象獲取到具體參數(shù)并解析得到參數(shù)值
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//綁定參數(shù)名稱發(fā)現(xiàn)器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//從providedArgs中嘗試獲取到參數(shù)名
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//如果方法參數(shù)解析器不支持parameter,則拋出異常
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
//使用參數(shù)解析器解析參數(shù)獲取到值,下面會重點(diǎn)分析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
MethodParameter[] parameters = getMethodParameters();這里獲取的 MethodParameter[] parameters如下圖所示:

參數(shù)解析器組合對象( this.resolvers)列表如下所示:

為什么稱之為參數(shù)解析器組合對象?其實(shí)這里的this.resolvers并不是具體的參數(shù)解析器而是argumentResolvers、argumentResolverCache組合而成的HandlerMethodArgumentResolverComposite!
可以看到起還有argumentResolverCache屬性,其值列表如下:

默認(rèn)argumentResolverCache是一個容量為256的ConcurrentHashMap,是HandlerMethodArgumentResolverComposite的成員變量:
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
這個argumentResolverCache是在動態(tài)改變,其在判斷是否支持paramter的方法中會改變,HandlerMethodArgumentResolverComposite.getArgumentResolver源碼如下:
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//如果緩存中有,則直接返回
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
//如果緩存中沒有就嘗試從解析器列表中獲取一個支持parameter的,并將解析器 parameter放入緩存
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
為什么要有argumentResolverCache ?你可以沒有,但是你就需要每次從argumentResolvers遍歷尋找支持當(dāng)前MethodParameter的參數(shù)解析器!之所以保存一份鍵值對數(shù)據(jù)到argumentResolverCache ,就是為了下次不用尋找,就是為了更快!
ok ,引申多了。咱們繼續(xù)回去看如何解析參數(shù)獲取到參數(shù)值!
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
這里會調(diào)用HandlerMethodArgumentResolverComposite.resolveArgument方法:
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//這里獲取具體的、實(shí)際的參數(shù)解析器!
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
獲取的實(shí)際的參數(shù)解析器如下所示(是RequestResponseBodyMethodProcessor):

調(diào)用RequestResponseBodyMethodProcessor.resolveArgument解析參數(shù):
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//這里獲取具體的、實(shí)際的參數(shù)解析器!
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
獲取方法參數(shù)變量名稱String name = Conventions.getVariableNameForParameter(parameter);Conventions.getVariableNameForParameter方法源碼如下:
public static String getVariableNameForParameter(MethodParameter parameter) {
Assert.notNull(parameter, "MethodParameter must not be null");
Class<?> valueClass;
boolean pluralize = false;
String reactiveSuffix = "";
//判斷參數(shù)類型是不是數(shù)組
if (parameter.getParameterType().isArray()) {
valueClass = parameter.getParameterType().getComponentType();
pluralize = true;
}
// 判斷是不是集合類型
else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
valueClass = ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric();
if (valueClass == null) {
throw new IllegalArgumentException(
"Cannot generate variable name for non-typed Collection parameter type");
}
pluralize = true;
}
else {
//獲取參數(shù)類型,這里是com.alibaba.fastjson.JSONObject
valueClass = parameter.getParameterType();
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(valueClass);
if (adapter != null && !adapter.getDescriptor().isNoValue()) {
reactiveSuffix = ClassUtils.getShortName(valueClass);
valueClass = parameter.nested().getNestedParameterType();
}
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name + reactiveSuffix);
}
拿到參數(shù)變量名與參數(shù)值后,就會進(jìn)行數(shù)據(jù)綁定過程。在這個過程中會使用binderFactory創(chuàng)建WebDataBinder對象,然后使用WebBindingInitializer對其進(jìn)行初始化。
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
if (arg != null) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
if (mavContainer != null) {
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
}
}
首先我們看一下WebDataBinder實(shí)例對象創(chuàng)建過程。DefaultDataBinderFactory.createBinder方法源碼如下:
public final WebDataBinder createBinder(
NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
//創(chuàng)建WebDataBinder 實(shí)例
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
//如果初始化器不為null,進(jìn)行初始化
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
//這是擴(kuò)展接口,可以用戶自定義以進(jìn)行更深入的初始化
initBinder(dataBinder, webRequest);
return dataBinder;
}
繼續(xù)跟createBinderInstance(target, objectName, webRequest);,其會走到ServletRequestDataBinderFactory.createBinderInstance方法,如下所示:

可以發(fā)現(xiàn)器創(chuàng)建了一個ExtendedServletRequestDataBinder實(shí)例對象,其類繼承圖如下:

創(chuàng)建ExtendedServletRequestDataBinder實(shí)例對象時,一路調(diào)用父類的構(gòu)造方法,最終跟到DataBinder類中:
public DataBinder(@Nullable Object target, String objectName) {
this.ignoreUnknownFields = true;
this.ignoreInvalidFields = false;
this.autoGrowNestedPaths = true;
this.autoGrowCollectionLimit = 256;
this.bindingErrorProcessor = new DefaultBindingErrorProcessor();
this.validators = new ArrayList();
this.target = ObjectUtils.unwrapOptional(target);
this.objectName = objectName;
}
創(chuàng)建完數(shù)據(jù)綁定器后,就使用初始化器對其進(jìn)行初始化,ConfigurableWebBindingInitializer.initBinder方法如下所示:
public void initBinder(WebDataBinder binder) {
binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
if (this.directFieldAccess) {
binder.initDirectFieldAccess();
}
if (this.messageCodesResolver != null) {
binder.setMessageCodesResolver(this.messageCodesResolver);
}
if (this.bindingErrorProcessor != null) {
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
}
//如果target不為null且校驗器不為空,就綁定校驗器
if (this.validator != null && binder.getTarget() != null &&
this.validator.supports(binder.getTarget().getClass())) {
binder.setValidator(this.validator);
}
//綁定類型轉(zhuǎn)換服務(wù)類
if (this.conversionService != null) {
binder.setConversionService(this.conversionService);
}
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
propertyEditorRegistrar.registerCustomEditors(binder);
}
}
}
代碼如下所示,初始化完WebDataBinder后,就嘗試使用binder的校驗器對parameter進(jìn)行校驗(如果參數(shù)使用了@Valid注解或者以Valid開頭的注解)。校驗完后就會獲取org.springframework.validation.BeanPropertyBindingResult,如果BeanPropertyBindingResult有錯誤且你并沒有用一個Errors對象的參數(shù)接收異常,那么就會拋出MethodArgumentNotValidException異常!
public void initBinder(WebDataBinder binder) {
binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
if (this.directFieldAccess) {
binder.initDirectFieldAccess();
}
if (this.messageCodesResolver != null) {
binder.setMessageCodesResolver(this.messageCodesResolver);
}
if (this.bindingErrorProcessor != null) {
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
}
//如果target不為null且校驗器不為空,就綁定校驗器
if (this.validator != null && binder.getTarget() != null &&
this.validator.supports(binder.getTarget().getClass())) {
binder.setValidator(this.validator);
}
//綁定類型轉(zhuǎn)換服務(wù)類
if (this.conversionService != null) {
binder.setConversionService(this.conversionService);
}
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
propertyEditorRegistrar.registerCustomEditors(binder);
}
}
}
數(shù)據(jù)綁定這一塊就是參數(shù)從形參變?yōu)閷?shí)參的最后一步!如何把請求中的參數(shù)值賦給方法的形參,就是通過WebDataBinder 這個對象實(shí)現(xiàn)的!可以看下此時binder對象:

有了綁定結(jié)果后的binder:

繼續(xù)往下走mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());把綁定結(jié)果放到model中:

OK!回到HandlerMethodArgumentResolverComposite.resolveArgument!

然后繼續(xù)回到InvocableHandlerMethod.getMethodArgumentValues中:

因為本次請求的目標(biāo)方法只有一個參數(shù),則其會繼續(xù)返回到InvocableHandlerMethod.invokeForRequest,也就是說到此,① 已經(jīng)結(jié)束!

② 根據(jù)參數(shù)值反射調(diào)用目標(biāo)方法
InvocableHandlerMethod.doInvoke方法源碼如下:
protected Object doInvoke(Object... args) throws Exception {
//獲取橋接方法并使方法可以調(diào)用
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 獲取橋接方法以及對應(yīng)的bean 參數(shù)值,然后反射調(diào)用
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
這里就會反射調(diào)用目標(biāo)方法進(jìn)行處理!處理完后會再次返回,一直返回到ServletInvocableHandlerMethod.invokeAndHandle!

到此【3.1】結(jié)束!已經(jīng)調(diào)用了目標(biāo)方法并獲取到了目標(biāo)方法返回值!
【3.2】返回結(jié)果處理
ServletInvocableHandlerMethod.invokeAndHandle方法首先會反射調(diào)用目標(biāo)方法,然后拿到方法返回值。最后會根據(jù)returnValueHandlers對返回結(jié)果進(jìn)行處理!
this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
這里this.returnValueHandlers同樣是一個返回結(jié)果處理器組合對象,值列表如下:

① 獲取返回結(jié)果類型
HandlerMethod.getReturnValueType源碼如下:
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
}
ReturnValueMethodParameter是HandlerMethod的內(nèi)部類,并繼承自HandlerMethod.HandlerMethodParameter(沒錯,這貨也是HandlerMethod的內(nèi)部類):
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValue = returnValue;
}
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
super(original);
this.returnValue = original.returnValue;
}
@Override
public Class<?> getParameterType() {
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
}
@Override
public ReturnValueMethodParameter clone() {
return new ReturnValueMethodParameter(this);
}
}

② 選擇HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandlerComposite.handleReturnValue方法源碼如下:
private class ReturnValueMethodParameter extends HandlerMethodParameter {
@Nullable
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValue = returnValue;
}
protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
super(original);
this.returnValue = original.returnValue;
}
@Override
public Class<?> getParameterType() {
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
}
@Override
public ReturnValueMethodParameter clone() {
return new ReturnValueMethodParameter(this);
}
}
這里returnType如下所示,其是HandlerMethod$ReturnValueMethodParameter對象:

這里尋找到的Handler是RequestResponseBodyMethodProcessor:

還記得上面解析參數(shù)時,咱們獲取到的實(shí)際參數(shù)解析器也是這個RequestResponseBodyMethodProcessor!

也就是說RequestResponseBodyMethodProcessor就是用來處理@RequestBody和@ResponseBody的!它可以使用HttpMessageConverter從請求中讀數(shù)據(jù)賦給參數(shù),并可以把返回結(jié)果扔給響應(yīng)。HttpMessageConverter在這中間起到了什么作用呢?顧名思義,數(shù)據(jù)格式轉(zhuǎn)換!
其類結(jié)構(gòu)繼承圖如下:

③ 返回結(jié)果寫到outputMessage中
RequestResponseBodyMethodProcessor.handleReturnValue源碼如下:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
//設(shè)置請求已經(jīng)被處理
mavContainer.setRequestHandled(true);
//獲取一個ServletServerHttpRequest實(shí)例-構(gòu)造函數(shù)參數(shù)為HttpServletRequest
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
//獲取一個 實(shí)例ServletServerHttpResponse ,構(gòu)造函數(shù)參數(shù)為HttpServletResponse
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
記得在上面解析參數(shù)的時候調(diào)用過readWithMessageConverters方法,那時是從請求中獲取數(shù)據(jù)。這里返回響應(yīng)信息需要把返回結(jié)果寫到響應(yīng)體中。

AbstractMessageConverterMethodProcessor.writeWithMessageConverters源碼如下:
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;
//值類型,這里是class com.baby.healthcare.common.ResponseBean
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
//判斷是否Resource 或InputStreamSource
if (isResourceType(value, returnType)) {
outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
outputMessage.getServletResponse().getStatus() == 200) {
Resource resource = (Resource) value;
try {
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
body = HttpRange.toResourceRegions(httpRanges, resource);
valueType = body.getClass();
targetType = RESOURCE_REGION_LIST_TYPE;
}
catch (IllegalArgumentException ex) {
outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
}
}
}
MediaType selectedMediaType = null;
//響應(yīng)內(nèi)容類型
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
//獲取接收的MediaType
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request);
//獲取返回結(jié)果的MediaType
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
//如果body不為空,但是沒有合適的返回結(jié)果類型,則拋出異常
if (body != null && producibleTypes.isEmpty()) {
throw new HttpMessageNotWritableException(
"No converter found for return value of type: " + valueType);
}
//循環(huán)比較,從acceptableTypes找到適配producibleTypes的
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進(jìn)行排序
MediaType.sortBySpecificityAndQuality(mediaTypesToUse);
for (MediaType mediaType : mediaTypesToUse) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
}
else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
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) {
Set<MediaType> producibleMediaTypes =
(Set<MediaType>) inputMessage.getServletRequest()
.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
throw new HttpMessageNotWritableException(
"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
}
throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
}
關(guān)于MediaType、MimeType與ContType對照表可以參考博文:ContentType與MIME對照表

循環(huán)比較,從acceptableTypes找到適配producibleTypes的:
//循環(huán)比較,從acceptableTypes找到適配producibleTypes的
List<MediaType> mediaTypesToUse = new ArrayList<>();
for (MediaType requestedType : acceptableTypes) {
for (MediaType producibleType : producibleTypes) {
if (requestedType.isCompatibleWith(producibleType)) {
mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));
}
}
}
請求接收的內(nèi)容類型與返回響應(yīng)的內(nèi)容類型如下所示:

MediaType.sortBySpecificityAndQuality(mediaTypesToUse);對mediaTypesToUse進(jìn)行排序,排序后的效果如下所示:

尋找合適的轉(zhuǎn)換器把body寫到outputMessage中:
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;
}
}
這里遍歷的messageConverters如下所示:

genericConverter.write(body, targetType, selectedMediaType, outputMessage);最后使用MappingJackson2HttpMessageConverter把body寫到outputMessage中。其類結(jié)構(gòu)繼承示意圖如下:

往響應(yīng)輸出流中寫完返回結(jié)果并flush后就會依次返回,此時【3.2】HandlerMethodReturnValueHandlerComposite.handleReturnValue返回結(jié)果處理執(zhí)行完畢!
然后返回到ServletInvocableHandlerMethod.invokeAndHandle,此時【3】執(zhí)行完畢!
【4】RequestMappingHandlerAdapter.getModelAndView嘗試獲取視圖對象
RequestMappingHandlerAdapter.getModelAndView方法源碼如下:
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
//更新model
modelFactory.updateModel(webRequest, mavContainer);
//如果請求已經(jīng)處理完,則直接返回,不會再嘗試創(chuàng)建mav
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
① modelFactory.updateModel(webRequest, mavContainer);更新model
- @SessionAttributes注解的屬性鍵值對放到session中;
- 如果請求沒有處理完畢,則嘗試更新model中的BindingResult
② 如果請求處理完畢,則直接返回null
如下圖所示,在【3.2】-③中handleReturnValue首先將requestHandled設(shè)置為true。那么自然不會往下走去獲取視圖名并嘗試解析

【5】ServletWebRequest.requestCompleted
ServletWebRequest類繼承示意圖如下:

其會直接調(diào)用AbstractRequestAttributes.requestCompleted,方法源碼如下:
#標(biāo)記這個請求已經(jīng)被完成
##調(diào)用所有的銷毀回調(diào)方法
##更新請求過程中訪問到的會話屬性
public void requestCompleted() {
executeRequestDestructionCallbacks();
updateAccessedSessionAttributes();
this.requestActive = false;
}
AbstractRequestAttributes.executeRequestDestructionCallbacks源碼如下,其會遍歷requestDestructionCallbacks并依次執(zhí)行每個Runnable。
private void executeRequestDestructionCallbacks() {
//這里使用synchronized 保證每個runnable 只被調(diào)用一次
synchronized (this.requestDestructionCallbacks) {
for (Runnable runnable : this.requestDestructionCallbacks.values()) {
runnable.run();
}
this.requestDestructionCallbacks.clear();
}
}
然后依次返回到RequestMappingHandlerAdapter.handleInternal也就是【1】-②:

如果響應(yīng)頭中不包含緩存控制Cache-Control,則嘗試對response進(jìn)行Cache-Control設(shè)置:
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
然后會返回到AbstractHandlerMethodAdapter.handle方法,然后回到DispatcherServlet.doDispatch,這是獲取到的MV為null。

【6】DispatcherServlet剩下的處理
① applyDefaultViewName(processedRequest, mv);嘗試獲取視圖名字
源碼如下所示,這里MV為null,自然不存在view name。
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
② mappedHandler.applyPostHandle(processedRequest, response, mv);方法后置處理
其實(shí)就是執(zhí)行攔截器的后置方法postHandle,HandlerExecutionChain.applyPostHandle源碼如下:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
③ processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
如果存在異常,則MV為 ((ModelAndViewDefiningException) exception).getModelAndView();然后進(jìn)行render(mv, request, response);;
如果不存在異常,且MV不為null,則進(jìn)行render(mv, request, response);;
如果MV不存在,則不會進(jìn)行render(mv, request, response);;,其會直接調(diào)用mappedHandler.triggerAfterCompletion(request, response, null);。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
攔截器的完成方法afterCompletion調(diào)用,HandlerExecutionChain.triggerAfterCompletion方法源碼如下:
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
最后執(zhí)行finally 里面的邏輯:
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
到此這篇關(guān)于詳解SpringBoot中添加@ResponseBody注解會發(fā)生什么的文章就介紹到這了,更多相關(guān)SpringBoot添加@ResponseBody內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于Mybatis實(shí)體別名支持通配符掃描問題小結(jié)
MyBatis可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄,這篇文章主要介紹了Mybatis實(shí)體別名支持通配符掃描的問題,需要的朋友可以參考下2022-01-01
詳解springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑
本文主要介紹了springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08
spring cloud config 配置中心快速實(shí)現(xiàn)過程解析
這篇文章主要介紹了spring cloud config 配置中心快速實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08
springboot發(fā)送郵件功能的實(shí)現(xiàn)代碼
發(fā)郵件是一個很常見的功能,在java中實(shí)現(xiàn)需要依靠JavaMailSender這個接口,今天通過本文給大家分享springboot發(fā)送郵件功能的實(shí)現(xiàn)代碼,感興趣的朋友跟隨小編一起看看吧2021-07-07
SpringMVC數(shù)據(jù)輸出相關(guān)知識總結(jié)
今天帶大家學(xué)習(xí)SpringMVC的相關(guān)知識,文中對SpringMVC數(shù)據(jù)輸出作了非常詳細(xì)的代碼示例,對正在學(xué)習(xí)的小伙伴們很有幫助,需要的朋友可以參考下2021-06-06
Spring Boot兩種配置文件properties和yml區(qū)別
這篇文章主要為大家介紹了java面試中常見問到的Spring Boot兩種配置文件properties和yml區(qū)別解答,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Java 兩種延時thread和timer詳解及實(shí)例代碼
這篇文章主要介紹了Java 兩種延時thread和timer詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02

