文章目錄
- HandlerAdapter
- 一、HandlerAdapter
- 二、HandlerAdapter 繼承體系
- 2.1 AbstractHandlerMethodAdapter
- 2.2 SimpleControllerHandlerAdapter
- 2.3 HttpRequestHandlerAdapter
- 2.4 SimpleServletHandlerAdapter
- 2.5 AnnotationMethodHandlerAdapter
- 2.6 RequestMappingHandlerAdapter
- 三、RequestMappingHandlerAdapter 源碼分析
- 3.1 AbstractHandlerMethodAdapter
- 3.2 supportsInternal
- 3.3 handleInternal
- 3.4 invokeHandlerMethod
- 3.5 getDataBinderFactory
- 3.6 getModelFactory
- 四、示例
- 五、RequestMappingHandlerAdapter 初始化
- 六、小結
- 七、參考
HandlerAdapter
- HandlerAdapter 的作用是封裝 HandlerMapping 所獲取到的 handler ,並調用 handler 處理清除,返回 ModelAndView 對象。
一、HandlerAdapter
1.1 HandlerAdapter設計思想?
- HandlerAdapter 是處理器適配器,它採用適配器模式來適配不同類型的 handler 請求處理器,這裏首先思考爲什麼要這樣設計,在 DispatchServlet 中,DispatchServlet直接獲取到 handler 之後處理請求不行嗎?爲什麼還需將 handler 包裝成 HandlerAdapter 之後再進入處理請求的邏輯。這裏是爲了解耦,試想,假設沒有 HandlerAdapter ,並且我們有三種 handler類型,那麼三種類型來處理請求的方式各不一樣,代碼很可能是如下形式:
if(handler instanceof Servlet){
(Servlet)handler.service();
}else if(handler instanceof HandlerMethod){
(ServletInvocableHandlerMethod)handler.invokeAndHandle();
}else if (handler instanceof Controller){
((Controller) handler).handleRequest();
}
- 那當我們後續增加一種類型的時候呢?勢必這段邏輯是需要修改的,違反開閉原則(對擴展開發,對修改關閉);反之有了HandlerAdapter之後,不管什麼處理器,都需要實現 HandlerAdapter 的接口標準,DispatchServlet只需要找到支持 handler 的
HandlerAdapter就行了,然後調用 HandlerAdapter 中定義好的接口方法即可,擴展也是沒有任何問題,這也是使用適配器模式的優勢。
1.2 接口定義
- HandlerAdapter是一個接口,
public interface HandlerAdapter {
//HandlerAdapter 是否支持該handler
boolean supports(Object handler);
//HandlerAdapter 處理請求,返回一個ModelAndView
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//和緩存有關,不支持的話可以簡單返回-1
long getLastModified(HttpServletRequest request, Object handler);
}
1.3 初始化 HandlerAdapter
- 前面文章分析過,在 DispatcherServlet 的初始化策略的時候會初始化 HandlerAdapter,如下:
protected void initStrategies(ApplicationContext context) {
//省略 ...
initHandlerAdapters(context);
//省略 ...
}
- initHandlerAdapters : 方法會首先判斷是否探測全部的HandlerAdapter,如果是,則則找出全部實現類,反之則找出指定實現類,如果沒找到就加載默認策略。
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
//1.如果檢測全部的HandlerAdapters,就找出全部匹配的
if (this.detectAllHandlerAdapters) {
// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
// We keep HandlerAdapters in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
//2.如果不檢測全部的,就找出指定的
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerAdapter later.
}
}
//3.如果麼有找到,就加載默認的
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
}
}
- PS:
detectAllHandlerAdapters 對應一個配置,默認爲true,加載全部 HandlerAdapter 類型的實例
1.4 獲取 HandlerAdapter
- DispatcherServlet 在 doDispatch 方法中處理請求時獲取 HandlerAdapter,通過 getHandlerAdapter 方法獲取:
//DispatcherServlet#getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//1.遍歷初始化好的策略屬性handlerAdapters
for (HandlerAdapter ha : this.handlerAdapters) {
//2.找到支持目標處理器的則返回
if (ha.supports(handler)) {
return ha;
}
}
//3.沒有則拋出異常
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- 代碼流程很清晰,從策略屬性 handlerAdapters 集合中遍歷,尋找能夠支持 handler 處理器的適配器,尋找到了就返回,最後也是由這個Adapter適配器去負責處理目標方法,加鎖我們新增了一種 handler 處理器類型,那麼針對該類的寫一種適配器實現就行了,對於原本的代碼沒有任何影響。
二、HandlerAdapter 繼承體系
- 下面是HandlerAdapter的子類繼承關係
2.1 AbstractHandlerMethodAdapter
- 抽象類 , HandlerAdapter的抽象實現,支持 HandlerMethod
2.2 SimpleControllerHandlerAdapter
- 簡單控制器處理器適配器,適配 Controller 類型的 handler。這裏的控制器的實現是一個簡單的控制器接口的實現,代碼比較簡單,核心方法如下。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
2.3 HttpRequestHandlerAdapter
- HTTP請求處理器適配器,適配 HttpRequestHandler 類型的處理器。它簡單的將HTTP請求對象和響應對象傳遞給HTTP請求處理器的實現,不需要返回值,它主要應用於基於HTTP的遠程調用的實現上,代碼也比較簡單,核心代碼如下:
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
2.4 SimpleServletHandlerAdapter
- 適配 Servlet 類型的 handler
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
2.5 AnnotationMethodHandlerAdapter
- 已經被標記 @Deprecated ,就不關注了;
2.6 RequestMappingHandlerAdapter
- 非抽象類,繼承自AbstractHandlerMethodAdapter , AbstractHandlerMethodAdapter的擴展、支持 @RequestMapping 的HandlerMethod , 雖然寫在最後,但卻是最需要關注的一個實現類;
三、RequestMappingHandlerAdapter 源碼分析
3.1 AbstractHandlerMethodAdapter
- AbstractHandlerMethodAdapter 是接口的抽象骨架實現,定義了部分方法,代碼不多,註釋如下:
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
//最大數值代表最低優先級
private int order = Ordered.LOWEST_PRECEDENCE;
//構造方法
public AbstractHandlerMethodAdapter() {
// no restriction of HTTP methods by default
super(false);
}
//設置優先級
public void setOrder(int order) { this.order = order; }
@Override
public int getOrder() { return this.order; }
//判斷適配器是否支持對應的Handler處理器方法
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//子類重寫,判斷是否支持對應的處理器方法
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
//處理請求,注意參數的handler期望是一個HandlerMethod
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//子類重寫,處理的核心方法
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
//子類重寫,getLastModifiedInternal
protected abstract long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod);
}
- 查看 AbstractHandlerMethodAdapter 可知子類需要實現兩個關鍵的模板方法:supportsInternal 和 handleInternal ;
- RequestMappingHandlerAdapter 是 HandlerAdapter 最複雜的一個子類,也是最需要關注分析的子類,源碼也較多,從 AbstractHandlerMethodAdapter 出發,我們關注幾個需要重寫的三個方法(都標記了子類重寫),在 RequestMappingHandlerAdapter 中 supportsInternal 和 getLastModifiedInternal 都是簡單實現,前者返回true,後者返回-1。因此主要分析 handleInternal 方法。
3.2 supportsInternal
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
- supportsInternal 返回true表示,只要處理器類型是 HandlerMethod類型,RequestMappingHandlerAdapter 就能夠支持;
3.3 handleInternal
- handleInternal 是 RequestMappingHandlerAdapter 處理請求的注意邏輯,大體分爲下面三步:
1.參數解析(解析參數,比較複雜)
2.使用處理器處理請求 (反射調用handleMethod )
3.處理返回值,(將不同類型的返回值統一處理成ModelAndView)
- RequestMappingHandlerAdapter#handleInternal,我們先從整體上來看,方法的作用就是處理請求,然後返回一個 ModelAndView。
//RequestMappingHandlerAdapter#handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
//1.校驗請求方法檢查是否支持
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 需要對 session 進行同步處理
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...
//不需要對 session 進行同步處理就直接對 HandlerMethod 進行處理
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// 請求頭沒有Cache-Control,直接返回,包含則需要處理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
//返回模型示圖
return mav;
}
- handleInternal 主要做了兩部分處理:一個是判斷當前是否對session進行同步處理,如果需要,則對其調用進行加鎖,反之直接調用;另一個是判斷請求頭中是否包含Cache-Control請求頭,如果不包含則設置其Cache立即失效。對於 HandlerMethod 的具體處理是在 invokeHandlerMethod 方法中進行的;
3.4 invokeHandlerMethod
- invokeHandlerMethod 是處理請求的主體邏輯,對於 HandlerMethod 的調用處理:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//1.獲取全局的 InitBinder 和局部的 InitBinder,用於參數綁定, 這些方法用於參數綁定
//注意全局是在 @ControllerAdvice 類中聲明的 ,局部的是目標處理類中聲明的,比如@Controller中聲明的,
//具體可以參考4.2示例和第五大點初始化部分代碼解析
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//2.獲取容器中全局的 ModelAttribute 和局部的 ModelAttribute,這些配置的方法將會在目標方法調用之前進行調用
//注意全局是在 @ControllerAdvice 類中聲明的,局部的是目標處理類中聲明的,比如@Controller中聲明的,
//具體可以參考4.2示例和第五大點初始化部分代碼解析
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//3.將 handlerMethod 封裝爲一個 ServletInvocableHandlerMethod 對象, 該對象用於對當前request的整體調用流程進行了封裝
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
//4.設置當前容器中配置的所有 ArgumentResolver 參數解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
//5.設置當前容器中配置的所有 ReturnValueHandler 返回值處理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
//6.將 WebDataBinderFactory 設置到 ServletInvocableHandlerMethod 中
invocableMethod.setDataBinderFactory(binderFactory);
//7.設置 ParameterNameDiscoverer ,用於發現參數的名稱
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//8.initModel() 方法調用前面獲取到的 @ModelAttribute 標註的方法,使@ModelAttribute標註的方法能夠在目標Handler之前調用
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//9.獲取當前的AsyncWebRequest,判斷目標 handler 的返回值是否爲 WebAsyncTask 或 DefferredResult
//如果是二者之一則當前請求的處理是異步的。當前請求會將Controller中封裝的業務邏輯放到一個線程池中
//進行調用,待該調用有返回結果之後再返回到response中。
//異步的優點在於解放請求分發的線程,從而處理更多的請求,只有目標任務完成後纔會回來將該異步任務結果返回
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//10.封裝異步任務的線程池,request 和 interceptors 到 WebAsyncManager中
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//11.判斷當前請求是否有異步任務結果,如果有結果則對異步任務結果進行封裝
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//12.對請求參數進行處理,調用目標 HandlerMethod,並且將返回值封裝爲一個ModelAndView對象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//13.對封裝的 ModelAndView 進行處理,判斷當前請求是否進行了重定向,如果進行了重定向,
//還會判斷是否需要將 FlashAttributes 封裝到新的請求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
//14.調用request destruction callbacks和對SessionAttributes進行處理
webRequest.requestCompleted();
}
}
- invokeHandlerMethod 的大體處理流程包括 :
1.獲取 @InitBinder註解的的參數綁定轉換器
2.獲取當前容器中使用@ModelAttribute標註但沒有使用 @RequestMapping 標註的方法,並且在調用目標方法之前調用這些方法,參考4.2小結和第五點初始化部分代碼解析
3.判斷目標 handler 返回值是否使用了 WebAsyncTask 或 DefferredResult 封裝,如果封裝了,則按照異步任務的方式進行執行;
4.處理請求參數,調用目標方法和處理返回值。
- 這裏面的代碼註釋大部分是參考了參考文章[4]的
3.5 getDataBinderFactory
- getDataBinderFactory 處理標註@InitBinder的方法,用於參數綁定
private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
Class<?> handlerType = handlerMethod.getBeanType();
//1.緩存中獲取當前 handler 所需要的 InitBinder 方法,第一次緩存是沒有的,這個緩存在Bean的生命周
//期不會初始化,只會在第一次訪問的時候初始化好,後續使用就直接用緩存
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
//2.緩存未找到,就掃描 handlerType 類型的Bean去尋找註解了 @InitBinder 的方法,因此此類方法需
//要在目標處理類裏面聲明,參考 4.1
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
//2.得到全部的 initBinderMethods 方法,先添加全局的,全局的是 @ControllerAdvice 類裏面去找,不過這裏全
//局的已經初始化好在 initBinderAdviceCache 屬性裏面,只需要遍歷處理,至於全局的初始化時機參考第五點的分析
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
//3.再添加 HandlerMethod 所在Bean中的 InitBinder 方法,即局部的 InitBinder
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
//4.將 InitBinder 封裝到 InitBinderDataBinderFactory 中
return createDataBinderFactory(initBinderMethods);
}
-
getDataBinderFactory 方法主要用於參數綁定相關,內部會獲取全局和局部兩種類型的 InitBinder ,全局類型的 InitBinder 需要在類上使用@ControllerAdvice進行標註,並且聲明方法上使用 @InitBinder 進行標註;局部的是當前 handler 所在類中的使用 @InitBinder 標註的方法(比如Controller中的)。這兩種InitBinder都會執行,只不過全局類型的InitBinder會先於局部類型的InitBinder執行。
-
參數綁定這塊代碼還是有點晦澀的,爲此在後面搭配了一個簡單的示例,在 4.1 的自定義參數綁定,結合調試看的會更清晰點。
3.6 getModelFactory
- getModelFactory方法會獲取 @ModelAttribute 標註的方法,此類方法會在目標方法之前執行; (關於 @ModelAttribute 的方法可以參考4.2中的示例)
private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
Class<?> handlerType = handlerMethod.getBeanType();
//1.和獲取綁定方法套路一樣,先從緩存獲取,第一次沒有緩存,後續都可以使用緩存加快速度
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<>();
//2.先處理全局的
// Global methods first
this.modelAttributeAdviceCache.forEach((clazz, methodSet) -> {
if (clazz.isApplicableToBeanType(handlerType)) {
Object bean = clazz.resolveBean();
for (Method method : methodSet) {
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
}
});
//3.再到目標Bean處理局部的
for (Method method : methods) {
Object bean = handlerMethod.getBean();
attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
}
return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
}
- 和前面的 InitBinder 類似,全局的在 @ControllerAdvice 中,局部的是處理器所在的類中,全部找出包裝之後返回。
四、示例
4.1 自定義參數綁定
- 自定義一個局部的參數綁定器, 代碼如下,其實就是將傳來的參數按照自己的方式用冒號分割,然後賦給屬性:
//參數類型
@Data
public class Person {
private String username;
private String address;
}
//控制器,接受 Person 類型的參數
@RestController
public class BinderTestController {
@RequestMapping("/binderTest")
public String binderTest(@RequestParam("person") Person person) {
String result = person.toString() + " " + new Date();
System.out.println(result);
return result;
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Person.class, new PersonEditor());
}
}
//參數綁定,將參數分割,賦值給 Person
public class PersonEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
Person p = new Person();
if (text != null) {
String[] items = text.split(":");
p.setUsername(items[0]);
p.setAddress(items[1]);
}
setValue(p);
}
@Override
public String getAsText() {
return getValue().toString();
}
}
測試:
測試輸入:http://localhost:8080/binderTest?person=mozping:shenzhen,即可
打印並返回:Person(username=mozping, address=shenzhen) Fri Nov 15 15:28:19 CST 2019
- 調試圖:
- 圖中可以看到,尋找到的 handler 類型就是控制器 BinderTestController,尋找到的 methods 只有一個,就是 initBinder,沒有全局的@InitBinder,因此全局的綁定方法是空,最後只找到我們自定義的這個。
4.2 自定義ModelAttribute方法
- 自定義全局和局部的 @ModelAttribute方法,下面是局部的,ModelAttribute 方法會在控制器的其他方法之前執行,在3.4的 invokeHandlerMethod 方法中解釋過了,我們看代碼
@RestController
public class ModelAttributeController {
@ModelAttribute
public void myModel(Model model) {
System.out.println("ModelAttribute method ------------ ");
}
@RequestMapping("/modelAttr")
public String modelAttr(@RequestParam("name") String name) {
System.out.println("modelAttr execute ... " + name);
return "modelAttr";
}
}
- @ControllerAdvice 聲明定義全局的 @ModelAttribute 方法:
//然後是一個全局的 @ModelAttribute 方法,必須使用 @ControllerAdvice 聲明
@ControllerAdvice
public class GlobalModelAttribute {
@ModelAttribute
public void myModel(Model model) {
System.out.println("Global ModelAttribute method ------------ ");
}
}
請求:http://localhost:8080/modelAttr?name=mozping
打印:
Global ModelAttribute method ------------
ModelAttribute method ------------
modelAttr execute ... mozping
- 可以看到 @ModelAttribute 的方法會在目標方法之前被執行,注意如果有多個 @RequestMapping 方法,@ModelAttribute都會在前面攔截執行,需要謹慎使用。
- 非全局的 @ModelAttribute 方法必須在目標處理器類裏面聲明,框架也是從 bean裏面去尋找的,全局的則需要使用 @ControllerAdvice 聲明,全局的會優先執行
- 在 @ModelAttribute 方法的參數中有一個 Model 形參,因此可以對示圖做一些修改,更多關於 @ModelAttribute 可以閱讀參考文章[5]
五、RequestMappingHandlerAdapter 初始化
-
前面的 @InitBinder 和 @ModelAttribute 都有全局和局部的,局部的就是去Bean裏面去找,而全局的直接遍歷initBinderAdviceCache 和 modelAttributeAdviceCache 屬性集合處理,這些全局的在Bean的生命週期節點就已經掃描並保存好了。
-
RequestMappingHandlerAdapter 初始化部分放在最後,前面我們發現@InitBinder 和 @ModelAttribute 的掃描過程很類似,都是有全局的和局部的,局部的就定義在目標處理類的內部,代碼通過到目標Bean去掃描獲取,而全局的則定義在由 @ControllerAdvice 註解的類中,全局的 @InitBinder 和 @ModelAttribute分別緩存在屬性 initBinderAdviceCache 和 modelAttributeAdviceCache 中,這兩個緩存的集合在 Bean 初始化的時候就會去掃描並保存起來, 通過 initControllerAdviceCache() 方法來掃描,時機是在
InitializingBean接口的 afterPropertiesSet 方法,代碼如下:
5.1 執行時機
- InitializingBean#afterPropertiesSet方法的執行時機在屬性賦值完畢之後。
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
//將全局的 @ControllerAdvice 類掃描進來,裏面可能有全局的參數綁定器和 @ModelAttribute 方法
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);
}
}
5.2 掃描加載
- initControllerAdviceCache:加載全局的參數綁定器和@ModelAttribute方法,核心代碼如下:
private void initControllerAdviceCache() {
//1.找出全部@ControllerAdvice 的Bean,並排序好
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
//2.依次遍歷處理
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
//3.尋找不包含 RequestMapping註解 並且包含 ModelAttribute 註解 的方法
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
//4.不爲空則將 ModelAttribute方法添加到緩存
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
//5.處理參數綁定器,尋找包含 @InitBinder 註解的方法
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
//6.不爲空則將 InitBinder參數綁定方法添加到緩存
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
//7.requestResponseBodyAdviceBeans集合添加 RequestBodyAdvice 類型的Bean
if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
//8.requestResponseBodyAdviceBeans集合添加 RequestBodyAdvice 類型的Bean
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
- 下面想尋找全局增強的 ControllerAdvice 類型Bean的邏輯,很清晰,全局的 @InitBinder 和 @ModelAttribute 都是在 @ControllerAdvice 類裏面去掃描的;
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
List<ControllerAdviceBean> beans = new ArrayList<>();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class)) {
if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
beans.add(new ControllerAdviceBean(name, applicationContext));
}
}
return beans;
}
- @InitBinder方法 的加載條件是:包含@InitBinder註解
public static final MethodFilter INIT_BINDER_METHODS = method ->
AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
- @ModelAttribute方法 的加載條件是:包含 @ModelAttribute 註解且不包含 @RequestMapping 註解
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null));
六、小結
-
- 全局的 @InitBinder 和 @ModelAttribute 都是在 @ControllerAdvice 類裏面去尋找的,
-
- @InitBinder 則是尋找 @ControllerAdvice 裏面註解了@InitBinder的方法;
-
- @ModelAttribute 則是尋找 @ControllerAdvice 裏面註解了@ModelAttribute並且沒有註解 RequestMapping的方法
-
- 局部的 @InitBinder 和 @ModelAttribute 都是在目標處理器內部去尋找的,註解過濾要求是一樣的
-
- 全局的優先於局部的先執行
- 文章主要是梳理了 RequestMappingHandlerAdapter 的執行流程,配合示例重點看了參數綁定器和 @ModelAttribute的初始化以及加載時機和加載邏輯。
- RequestMappingHandlerAdapter 的執行流程大體上如下:
1.session同步處理
2.目標方法調用
( 獲取@InitBinder和@ModelAttribute -> 設置參數處理器 ->
設置返回值處理器 -> 參數發現器 -> 調用 @ModelAttribute 方法
-> 目標方法調用 -> 結果處理和返回 )
3.Cache-Control 處理
- 在 getDataBinderFactory 和 getModelFactory 方法裏面分別有createInitBinderMethod 和 createModelAttributeMethod 方法來包裝目標的@InitBinder 和 @ModelAttribute方法,二者都是返回 InvocableHandlerMethod 對象,他是 HandlerMethod 對象,也就是封裝了目標方法的類,createModelAttributeMethod 和 createModelAttributeMethod 裏面都涉及到 HandlerMethodArgumentResolver 處理器方法參數解析器策略,以及返回值處理器HandlerMethodReturnValueHandler等,在後續文章再分析。