RequestMappingHandlerAdapter含有大量的web基礎組件協助完成一整個請求的調度、處理
RequestMappingHandlerAdapter
適配@RequestMapping註解標註的Handler(HandlerMethod類型)
Spring3.1新增的一個適配器類,用於數據綁定、數據轉換、數據校驗、內容協商
因爲有了它的存在,使得開發者幾乎可以忘掉原生的Servlet API
Spring 5.0在把Servlet容器從必選項變成可選項後,即使切換了web容器(比如換成jetty),也能做到在使用層面上對開發者無感知
@since 3.1 實現了InitializingBean接口和BeanFactoryAware
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
// 唯一構造方法:默認註冊一些消息轉換器。開啓@EnableWebMvc後此默認行爲會被set方法覆蓋
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
} catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
// 此方法在容器啓動的時候會執行:掃描解析容器內的@ControllerAdvice等等
// 看起來代碼不多,但其實每個方法內部,都可謂是個龐然大物,請詳細觀察理解~~~~
@Override
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);
}
}
}
爲了突出重點,本文采用針對重點方法逐一描述的方式進行循序漸進的講解:
initControllerAdviceCache()
它是初始化@ControllerAdvice標註的Bean並解析其內部各部分(@ModelAttribute、@InitBinder、RequestBodyAdvice和ResponseBodyAdvice接口等)然後緩存起來的方法。
RequestMappingHandlerAdapter:
// ======================相關成員變量們======================
// 裝載RequestBodyAdvice和ResponseBodyAdvice的實現類們~
private List<Object> requestResponseBodyAdvice = new ArrayList<>();
// MethodIntrospector.selectMethods的過濾器。
// 這裏意思是:含有@ModelAttribute,但是但是但是不含有@RequestMapping註解的方法~~~~~
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method -> (!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) && AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
// 標註了註解@InitBinder的方法~~~
public static final MethodFilter INIT_BINDER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
// 存儲標註了@ModelAttribute註解的方法的緩存~~~~
private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();
// 存儲標註了@InitBinder註解的方法的緩存~~~~
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 拿到容器內所有的標註有@ControllerAdvice的組件們
// BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class)
// .filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null)
// .map(name -> new ControllerAdviceBean(name, context)) // 使用ControllerAdviceBean包裝起來,持有name的引用(還木實例化喲)
// .collect(Collectors.toList());
// 因爲@ControllerAdvice註解可以指定包名等屬性,具體可參見HandlerTypePredicate的判斷邏輯,是否生效
// 注意:@RestControllerAdvice是@ControllerAdvice和@ResponseBody的結合體,所以此處也會被找出來
// 最後Ordered排序
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
// 臨時存儲RequestBodyAdvice和ResponseBodyAdvice的實現類
// 它哥倆是必須配合@ControllerAdvice一起使用的~
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 又見到了這個熟悉的方法selectMethods~~~~過濾器請參照成員變量
// 含有@ModelAttribute,但是但是但是不含有@RequestMapping註解的方法~~~~~ 找到之後放在全局變量緩存起來
// 簡單的說就是找到@ControllerAdvice裏面所有的@ModelAttribute方法們
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
// 找標註了註解@InitBinder的方法~~~(和有沒有@RequestMapping木有關係了~~~)
// 找到@ControllerAdvice裏面所有的@InitBinder方法們
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
// 這兩個接口是Spring4.1 4.2提供的,實現了這兩個接口的
// 此處先放在requestResponseBodyAdviceBeans裏面裝着 最後放到全局緩存requestResponseBodyAdvice裏面去
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
// 這個意思是,放在該list的頭部。
// 因爲requestResponseBodyAdvice有可能通過set方法進來已經有值了~~~所以此處放在頭部
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
// 輸出debug日誌...略(debug日誌哦~)
if (logger.isDebugEnabled()) {
...
}
}
該步驟調理清晰,有4個作用:
找到容器內(包括父容器)所有的標註有@ControllerAdvice註解的Bean們緩存起來,然後一個個解析此種Bean
找到該Advice Bean內所有的標註有@ModelAttribute但沒標註@RequestMapping的方法們,緩存到modelAttributeAdviceCache裏對全局生效
找到該Advice Bean內所有的標註有@InitBinder的方法們,緩存到initBinderAdviceCache裏對全局生效
找到該Advice Bean內所有實現了接口RequestBodyAdvice/ResponseBodyAdvice們,最終放入緩存requestResponseBodyAdvice的頭部,他們會介入請求body和返回body
介紹完此initControllerAdviceCache方法後,繼續afterPropertiesSet()後續方法:初始化參數解析器、@InitBinder參數解析器、返回值解析器等。
@Override
public void afterPropertiesSet() {
...
// 初始化參數解析器
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 初始化@InitBinder的參數解析器
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);
}
}
getDefaultArgumentResolvers()
這個步驟作用是初始化HandlerMethodArgumentResolver,提供對方法參數的支持。
RequestMappingHandlerAdapter:
// Return the list of argument resolvers to use including built-in resolvers and custom resolvers provided via {@link #setCustomArgumentResolvers}.
// 返回內建的參數處理器們,以及用戶自定義的一些參數處理器(注意順序)
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());
// Type-based argument resolution
// 基於type類型的
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
// 用戶自定義的
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
// 兜底方案:這就是爲何很多時候不寫註解參數也能夠被自動封裝的原因
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
這裏使用的是ArrayList保存的,所以處理器都是有序的。最終會放進HandlerMethodArgumentResolverComposite使用Composite模式統一管理和使用~
getDefaultInitBinderArgumentResolvers()
這篇文章裏有介紹過@InitBinder註解標註的方法入參也是可以寫很多類型的參數的,需要注意的是它沒有上面支持的那麼全面,它支持的內容如下:
RequestMappingHandlerAdapter:
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
return resolvers;
}
從此處可以看到它具體支持哪些參數嘍,像什麼@RequestParam/@PathVariable/ServletRequest...等等都是支持的,但像什麼@ModelAttribute/@RequestBody/@RequestPart等等這些它就不支持了。
getDefaultReturnValueHandlers()
顧名思義,它是提供對HandlerMethod返回值的支持,最終使用的是HandlerMethodReturnValueHandlerComposite相同的模式管理和使用,此處是初始化邏輯,具體使用可點擊這裏。
RequestMappingHandlerAdapter:
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>();
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
// 返回值是ResponseBodyEmitter時候,得用reactiveAdapterRegistry看看是Reactive模式還是普通模式
// taskExecutor:異步時使用的線程池,使用當前類的 contentNegotiationManager:內容協商管理器
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(), this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
// 此處重要的是getMessageConverters()消息轉換器,一般情況下Spring MVC默認會有8個,包括`MappingJackson2HttpMessageConverter`
// 參見:WebMvcConfigurationSupport定的@Bean --> RequestMappingHandlerAdapter部分
// 若不@EnableWebMvc默認是隻有4個消息轉換器的哦~(不支持json)
// 此處的requestResponseBodyAdvice會介入到請求和響應的body裏(消息轉換期間)
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
// 當標註有@ModelAttribute或者@ResponseBody的時候 這裏來處理。顯然也用到了消息轉換器~
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
// 當返回的是個字符串/Map時候,這時候就可能有多個目的了(Multi-purpose)
// 比如字符串:可能重定向redirect、或者直接到某個view
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
// 自定義的返回值處理器
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
// 兜底:ModelAndViewResolver是需要你自己實現然後set進來的(一般我們不會自定定義)
// 所以絕大部分情況兜底使用的是ModelAttributeMethodProcessor表示,即使你的返回值裏木有標註@ModelAttribute
// 但你是非簡單類型(比如對象類型)的話,返回值都會放進Model裏
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
} else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
這個步驟完成後,整個RequestMappingHandlerAdapter的初始化就全部完成了。當然它還有一些屬性、方法等都沒有講述到,下面做出集中式的統一說明:
其它重要屬性、方法
屬性:
RequestMappingHandlerAdapter:
// ModelAndViewResolver木有內置實現,可自定義實現來參與到返回值到ModelAndView的過程(自定義返回值處理)
// 一般不怎麼使用,我個人也不太推薦使用
@Nullable
private List<ModelAndViewResolver> modelAndViewResolvers;
// 內容協商管理器 默認就是它嘍(使用的協商策略是HeaderContentNegotiationStrategy)
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
// 消息轉換器。使用@Bean定義的時候,記得set進來,否則默認只會有4個(不支持json)
// 若@EnableWebMvc後默認是有8個的,一般都夠用了
private List<HttpMessageConverter<?>> messageConverters;
// 它在數據綁定初始化的時候會被使用到,調用其initBinder()方法
// 只不過,現在一般都使用@InitBinder註解來處理了,所以使用較少
// 說明:它作用域是全局的,對所有的HandlerMethod都生效~~~~~
@Nullable
private WebBindingInitializer webBindingInitializer;
// 默認使用的SimpleAsyncTaskExecutor:每次執行客戶提交給它的任務時,它會啓動新的線程
// 並允許開發者控制併發線程的上限(concurrencyLimit),從而起到一定的資源節流作用(默認值是-1,表示不限流)
// @EnableWebMvc時可通過複寫接口的WebMvcConfigurer.getTaskExecutor()自定義提供一個線程池
private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync");
// invokeHandlerMethod()執行目標方法時若需要異步執行,超時時間可自定義(默認不超時)
// 使用上面的taskExecutor以及下面的callableInterceptors/deferredResultInterceptors參與異步的執行
@Nullable
private Long asyncRequestTimeout;
private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0];
private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0];
// @Since 5.0
private ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
// 對應ModelAndViewContainer.setIgnoreDefaultModelOnRedirect()屬性
// redirect時,是否忽略defaultModel 默認值是false:不忽略
private boolean ignoreDefaultModelOnRedirect = false;
// 返回內容緩存多久(默認不緩存) 參考類:WebContentGenerator
private int cacheSecondsForSessionAttributeHandlers = 0;
// 執行目標方法HandlerMethod時是否要在同一個Session內同步執行???
// 也就是同一個會話時,控制器方法全部同步執行(加互斥鎖)
// 使用場景:對同一用戶同一Session的所有訪問,必須串行化~~~~~~
private boolean synchronizeOnSession = false;
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Nullable
private ConfigurableBeanFactory beanFactory;
// ====================下面是各種緩存們====================
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();
方法:
RequestMappingHandlerAdapter:
... // 省略所有屬性的get/set方法
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
return -1;
}
// 因爲它只需要處理HandlerMethod這樣的Handler,所以這裏恆返回true 請參照父類的supportsInternal()鉤子方法
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
handleInternal
使用給定的處理器方法處理請求。
HandlerAdapter
的接口方法,處理請求的入口,最終返回一個ModelAndView
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 如果需要,在同步塊中執行invokeHandlerMethod,默認不需要
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
} else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 處理Cache-Control請求頭(若你沒有set
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
} else {
prepareResponse(response);
}
}
return mav;
}
剩下的便是本適配器最爲重要的一個方法:invokeHandlerMethod():
// 它的作用就是執行目標的HandlerMethod,然後返回一個ModelAndView
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 注意:此處只有try-finally 哦
// 因爲invocableMethod.invokeAndHandle(webRequest, mavContainer)是可能會拋出異常的(交給全局異常處理)
try {
// 最終創建的是一個ServletRequestDataBinderFactory,持有所有@InitBinder的method方法們
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 創建一個ModelFactory,@ModelAttribute啥的方法就會被引用進來
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 把HandlerMethod包裝爲ServletInvocableHandlerMethod,具有invoke執行的能力嘍
// 下面這幾部便是一直給invocableMethod的各大屬性賦值~~~
ServletInvocableHandlerMethod invocableMethod = 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);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 把上個request裏的值放進來到本request裏
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// model工廠:把它裏面的Model值放進mavContainer容器內(此處@ModelAttribute/@SessionAttribute啥的生效)
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
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);
// 它不管是不是異步請求都先用AsyncWebRequest 包裝了一下,但是若是同步請求
// asyncManager.hasConcurrentResult()肯定是爲false的~~~
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);
}
// 此處其實就是調用ServletInvocableHandlerMethod#invokeAndHandle()方法嘍
// 關於它你可以來這裏:https://fangshixiang.blog.csdn.net/article/details/98385163
// 注意哦:任何HandlerMethod執行完後都是把結果放在了mavContainer裏(它可能有Model,可能有View,可能啥都木有~~)
// 因此最後的getModelAndView()又得一看
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally {
webRequest.requestCompleted();
}
}
任何處理器執行完後,最終返回的的都是一個ModelAndView對象,這得益於getModelAndView()這個方法的適配處理:
// @Nullable:表示它返回的可以是個null哦~(若木有視圖,就直接不會render啦~因爲response已經寫入過值了)
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 把session裏面的內容寫入
modelFactory.updateModel(webRequest, mavContainer);
// Tips:若已經被處理過,那就返回null嘍~~(比如若是@ResponseBody這種,這裏就是true)
if (mavContainer.isRequestHandled()) {
return null;
}
// 通過View、Model、Status構造出一個ModelAndView,最終就可以完成渲染了
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) { // 是否是String類型
mav.setView((View) mavContainer.getView());
}
// 對重定向RedirectAttributes參數的支持(兩個請求之間傳遞參數,使用的是ATTRIBUTE)
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;
}
執行完HandlerMethod後得到一個ModelAndView,它可能是null(比如已被處理過),那最終交給DispatcherServlet就沒有後續處理了,否則會做視圖渲染:render()。
Spring MVC默認裝配了哪些HandlerAdapter呢?
開啓@EnableWebMvc:
總結
RequestMappingHandlerAdapter作爲HandlerAdapter適配模式的實現,由於@RequestMapping成爲了使用Spring MVC的幾乎唯一選擇,所以它成爲了實際意義上的標準實現