SpringMVC源碼解析之RequestMappingHandlerAdapter

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的幾乎唯一選擇,所以它成爲了實際意義上的標準實現

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