每篇一句
在沒有充分的知識作爲前提的情況下,即使行了萬里路,也不過是郵差而已。
前言
上篇文章介紹了HandlerAdapter
適配器的三種實現方式,分別實現了對"非主流"的三種控制器(Controller
/HttpRequestHandler
/Servlet
)的適配,由於此三種控制器本身非常源生和功能簡單,自然對應的適配器也非常好理解。
雖然說Spring MVC
一共兼具支持了4中控制器方式,但前三種方式可謂廉頗老矣,不客氣的說已經被後浪拍死在沙灘上,這就是爲何大多數Java程序員並不知道前三種控制器的原因。而此處指的"後浪"便是@RequestMapping
標註的控制器,它對應的適配器便是本文的主菜:RequestMappingHandlerAdapter
。
RequestMappingHandlerAdapter
它不僅僅之於HandlerAdapter
處理適配器是最爲重要的,甚至對於整個Spring MVC
框架來說,此類的重要程度也是top級別。它內部含有大量的web基礎組件(每個組件都是一個實用知識點)來協助完成一整個請求處理,因此它可以被描述爲單個請求的調度、處理中心。
RequestMappingHandlerAdapter
用於適配@RequestMapping
註解標註的Handler
(Handler
類型爲org.springframework.web.method.HandlerMethod
),繼承自父類AbstractHandlerMethodAdapter
。
它是自Spring3.1
新增的一個適配器類(HandlerMethod
也是3.1後出現的),擁有數據綁定、數據轉換、數據校驗、內容協商…等一系列非常高級的功能。因爲有了它的存在,使得開發者幾乎可以忘掉原生的Servlet API
並且使用起來更加的的心用手和更加的面向對象,所以我它的出現是具有里程碑意義的。
也正是因爲有了它對Servlet API
的屏蔽,Spring 5.0
在把Servlet
容器從必選項變成可選項可以平滑過渡:即使切換了web容器(比如換成基於netty的webflux),也能做到在使用層面上對開發者是無感知的,保證了使用者的體驗和降低了遷移成本,使得開發者不會抗拒reactive編程模式的到來。
它的類圖譜如下:
父類AbstractHandlerMethodAdapter
在上文已經有所描述,so本處就單刀直奔主題:
本類很大(1000+行代碼),成員屬性衆多,它是請求處理的集大成者,集成了所有的用於請求處理的功能模塊,所在在沒有前置基礎的情況下研究本類會非常吃力,但還好所有的(你沒聽錯,所有的)組件在我博客裏都有相關專題的詳細講解,若遇上明白的組件,可在我博客站內搜索關鍵字就能找到,本文相應的我也會對應的給些傳送門
因爲本來太大,所以按照傳統思路之上而下的介紹恐有不妥,因此本文從初始化流程出發,一步步揭開它的執行過程。
// @since 3.1 實現了InitializingBean接口和BeanFactoryAware
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
// 唯一構造方法:默認註冊一些消息轉換器。
// 開啓@EnableWebMvc後此默認行爲會被setMessageConverters()方法覆蓋
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());
}
// 此方法在此Bean初始化的時候會執行:掃描解析容器內的@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,並解析此Bean內部各部分(@ModelAttribute、@InitBinder
、RequestBodyAdvice
和ResponseBodyAdvice
接口)然後緩存起來。
關於
@ModelAttribute
、@InitBinder
等各部分,可站內搜索相關文章詳細瞭解
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參數解析器、返回值解析器等。
這一步總體來說就是對參數解析、返回值解析的支持。比如對@RequestParam、@PathVariable、@RequestBody、@ResponseBody等註解的支持
@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
,提供對方法參數的支持。也就是@RequestMapping
的handler上能寫哪些註解自動封裝參數,是由這裏決定的。
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模式統一管理和使用~
關於參數解析器
HandlerMethodArgumentResolver
的系列文章,你可以從這裏開始
getDefaultInitBinderArgumentResolvers()
這一步和上面非常相似,也是對方法參數的處理,不過它針對的是@InitBinder
這個註解標註的方法。可簡單理解爲它是@RequestMapping
的閹割版:支持的參數類型、註解都沒那麼多,支持內容如下:
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
等等這些它就不支持了(沒這必要嘛)。
關於參數解析器
@InitBinder
的系列文章,你可以從這裏開始
getDefaultReturnValueHandlers()
顧名思義,它是提供對HandlerMethod
返回值的支持(比如@ResponseBody、DeferredResult
等返回值類型)。多個返回值處理器最終使用的是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;
}
關於參數解析器
HandlerMethodReturnValueHandler
的系列文章,你可以從這裏開始
這個步驟完成後,整個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;
}
// 可以認爲這個就是`HandlerAdapter`的接口方法,是處理請求的入口 最終返回一個ModelAndView
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request); // 檢查方法
// Execute invokeHandlerMethod in synchronized block if required.
// 同一個Session下是否要串行,顯然一般都是不需要的。直接看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()
:
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()
。
關於
DispatcherServlet
的處理流程,站內搜索相關關鍵字也能找到相關文章的。
Spring MVC默認裝配了哪些HandlerAdapter呢?
開啓@EnableWebMvc
:
不開啓:
相關閱讀
web九大組件之—HandlerAdapter適配器模式實踐源碼分析【享學Spring MVC】
總結
RequestMappingHandlerAdapter
作爲HandlerAdapter
適配模式實現,由於@RequestMapping
成爲了使用Spring MVC
的近乎唯一選擇,所以它成爲了實際意義上的標準實現,深入瞭解掌握它其實非常有必要。
本類具有內容多、體系龐大、實現複雜等特點,源碼啃起來相較於其它模塊還是要更加費勁的,這主要是由於它"集成"進來的組件非常之多,而每一個基礎組件也是一個重要的且較難理解的知識點,因此路漫漫其修遠兮…多看幾遍吧,說不定啥時候就豁然開朗了。
按照國際慣例,最後送上一句:1024程序員節快樂。