web九大組件之---RequestMappingHandlerAdapter詳盡解析【享學Spring MVC】

每篇一句

在沒有充分的知識作爲前提的情況下,即使行了萬里路,也不過是郵差而已。

前言

上篇文章介紹了HandlerAdapter適配器的三種實現方式,分別實現了對"非主流"的三種控制器(Controller/HttpRequestHandler/Servlet)的適配,由於此三種控制器本身非常源生和功能簡單,自然對應的適配器也非常好理解。
雖然說Spring MVC一共兼具支持了4中控制器方式,但前三種方式可謂廉頗老矣,不客氣的說已經被後浪拍死在沙灘上,這就是爲何大多數Java程序員並不知道前三種控制器的原因。而此處指的"後浪"便是@RequestMapping標註的控制器,它對應的適配器便是本文的主菜:RequestMappingHandlerAdapter

RequestMappingHandlerAdapter它不僅僅之於HandlerAdapter處理適配器是最爲重要的,甚至對於整個Spring MVC框架來說,此類的重要程度也是top級別。它內部含有大量的web基礎組件(每個組件都是一個實用知識點)來協助完成一整個請求處理,因此它可以被描述爲單個請求的調度、處理中心。

RequestMappingHandlerAdapter

用於適配@RequestMapping註解標註的HandlerHandler類型爲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、@InitBinderRequestBodyAdviceResponseBodyAdvice接口)然後緩存起來。

關於@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個作用總結如下:

  1. 找到容器內(包括父容器)所有的標註有@ControllerAdvice註解的Bean們緩存起來,然後一個個解析此種Bean
  2. 找到該Advice Bean內所有的標註有@ModelAttribute但沒標註@RequestMapping的方法們,緩存到modelAttributeAdviceCache裏對全局生效
  3. 找到該Advice Bean內所有的標註有@InitBinder的方法們,緩存到initBinderAdviceCache裏對全局生效
  4. 找到該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程序員節快樂。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章