Spring源碼解析 -- SpringMvc原理

Spring源碼解析 -- 讀取bean元數據
spring源碼解析 -- 構造bean
spring源碼解析 -- 注入屬性
spring源碼解析 -- Spring Context
Spring源碼解析 -- AOP原理(1)
Spring源碼解析 -- AOP原理(2)
Spring源碼解析 -- SpringMvc原理java

源碼分析基於spring 4.3.x
本文經過閱讀Spring MVC的源碼,解析Spring MVC實現原理。本文不會深刻SpringMvc的細節,關注於分析SpringMvc的各個核心組件以及主要邏輯,以便你們深刻SpringMvc以及排查問題。web

關於閱讀源碼的思路,可參考 -- 如何閱讀java源碼spring

使用Spring MVC時,咱們常編寫一個spring-mvc.xml,xml中添加<mvc:annotation-driven/>標籤,這個標籤是由MvcNamespaceHandler處理。
MvcNamespaceHandler#parse -> AnnotationDrivenBeanDefinitionParser#parsejson

public BeanDefinition parse(Element element, ParserContext parserContext) {
	...

	RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);	// #1
	...

	RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);	// #3
	RuntimeBeanReference validator = getValidator(element, source, parserContext);	// #4
	RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);	// #5

	...

	ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);	// #6
	ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);	// #7
	ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);	// #8
	String asyncTimeout = getAsyncTimeout(element);	
	RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);	
	ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
	ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

	RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);	// #9
	...
	addRequestBodyAdvice(handlerAdapterDef);	// #10
	addResponseBodyAdvice(handlerAdapterDef);	// #11

	...
}
複製代碼

這裏主要準備一些SpringMvc使用的組件
#1 構造RequestMappingHandlerMapping
#3 處理conversion-service配置,構造ConversionService,默認使用FormattingConversionService處理數字,日期等屬性轉化
#4 處理validator配置,構造Validator,驗證參數
#5 處理message-codes-resolver配置,構造MessageCodesResolver,負責能夠綁定錯誤碼和錯誤信息
#6 處理message-converters配置,構造HttpMessageConverter,負責對http的body進行讀寫,解析工做,如MappingJackson2XmlHttpMessageConverter負責json格式的http請求。
這裏會根據Java環境中存在的json框架,添加對應的json處理類。例如maven引入了jackson框架,就會添加MappingJackson2HttpMessageConverter。
#7 處理argument-resolvers配置,構造HandlerMethodArgumentResolver,負責解析業務方法的參數
#8 處理return-value-handlers配置,構造HandlerMethodReturnValueHandler,負責處理業務方法返回值
#9 構造RequestMappingHandlerAdapter,RequestMappingHandlerAdapter負責使用HandlerMethod調用業務方法,而且處理方法參數和返回,前面步驟獲取的messageConverters,argumentResolvers,returnValueHandlers都要注入到RequestMappingHandlerAdapter的屬性中,同時,它也會添加默認的HandlerMethodArgumentResolver,HandlerMethodReturnValueHandler等組件類(參考RequestMappingHandlerAdapter#getDefaultReturnValueHandlers)。
#10 若是Java環境中存在jackson框架,構造JsonViewRequestBodyAdvice,JsonViewRequestBodyAdvice能夠在讀取http請求body先後作一些額外處理
#11 若是Java環境中存在jackson框架,構造JsonViewResponseBodyAdvice,JsonViewResponseBodyAdvice能夠在寫入http響應body先後作一些額外處理spring-mvc

RequestMappingHandlerMapping維護了URL,@RequestMapping註解以及業務方法(就是@RequestMapping標註的方法)之間的映射關係,父類AbstractHandlerMethodMapping實現了InitializingBean接口,AbstractHandlerMethodMapping#afterPropertiesSet -> initHandlerMethodsbash

protected void initHandlerMethods() {
	...
	String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
			getApplicationContext().getBeanNamesForType(Object.class));	// #1

	for (String beanName : beanNames) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			Class<?> beanType = null;
			try {
				beanType = getApplicationContext().getType(beanName);
			}
			...
			if (beanType != null && isHandler(beanType)) {	// #2
				detectHandlerMethods(beanName);	// #3
			}
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}
複製代碼

#1 獲取全部的beanName
#2 調用RequestMappingHandlerMapping#isHandler,檢查Class是否有@Controller/@RequestMapping註解
#3 構造並註冊HandlerMethod(封裝了業務方法和對應的Class)
這裏再也不深刻解析RequestMappingHandlerMapping,有興趣的同窗能夠繼續閱讀源碼。微信

DispatcherServlet

使用SpringMVC須要在web.xml添加一個DispatcherServlet處理的servlet,DispatcherServlet是實現SpringMVC的關鍵組件類。mvc

DispatcherServlet#initStrategies方法將加載Spring Context的SpringMvc組件類,如MultipartResolver,HandlerMapping,設置爲本身的屬性。
如initHandlerMappings方法獲取AnnotationDrivenBeanDefinitionParser中構造的RequestMappingHandlerMapping,做爲本身的屬性handlerMappings。app

DispatcherServlet的父類FrameworkServlet繼承於HttpServletBean,
doGet/doPost -> DispatcherServlet#doService -> doDispatch框架

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;
	HandlerExecutionChain mappedHandler = null;
	boolean multipartRequestParsed = false;

	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

	try {
		ModelAndView mv = null;
		Exception dispatchException = null;

		try {
			processedRequest = checkMultipart(request);	// #1
			multipartRequestParsed = (processedRequest != request);

			mappedHandler = getHandler(processedRequest);	// #2
			if (mappedHandler == null || mappedHandler.getHandler() == null) {
				noHandlerFound(processedRequest, response);
				return;
			}

			HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());	// #3

			String method = request.getMethod();	// #4
			boolean isGet = "GET".equals(method);
			if (isGet || "HEAD".equals(method)) {
				long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
				if (logger.isDebugEnabled()) {
					logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
				}
				if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
					return;
				}
			}

			if (!mappedHandler.applyPreHandle(processedRequest, response)) {	// #5
				return;	
			}

			// Actually invoke the handler.
			mv = ha.handle(processedRequest, response, mappedHandler.getHandler());	// #6

			if (asyncManager.isConcurrentHandlingStarted()) {	// #7
				return;
			}

			applyDefaultViewName(processedRequest, mv);
			mappedHandler.applyPostHandle(processedRequest, response, mv);	// #8
		}
		...
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);	// #9
	}
	...
}
複製代碼

#1 處理multipart類型的請求,如文件上傳
#2 主要是經過RequestMappingHandlerMapping#getHandler,查找對應的HandlerMethod,再構造HandlerExecutionChain。
HandlerExecutionChain能夠添加Interceptor攔截器,執行額外的流程,Cors請求的處理就是經過Interceptor實現的。
#3 這裏使用RequestMappingHandlerAdapter。HandlerAdapter負責使用handler處理requests
#4 處理http的lastModified標識
#5 調用HandlerInterceptor#preHandle
#6 使用HandlerAdapter處理請求
#7 判斷是否爲異步請求
#8 調用HandlerInterceptor#postHandle
#9 渲染ModelAndView以及處理異常

#6步驟 -> AbstractHandlerMethodAdapter#handle -> RequestMappingHandlerAdapter#handleInternal -> invokeHandlerMethod -> ServletInvocableHandlerMethod#invokeAndHandle

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);	// #1
	setResponseStatus(webRequest);	// #2

	if (returnValue == null) {	// #3
		if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
			mavContainer.setRequestHandled(true);
			return;
		}
	}
	else if (StringUtils.hasText(getResponseStatusReason())) {
		mavContainer.setRequestHandled(true);
		return;
	}

	mavContainer.setRequestHandled(false);
	try {
		this.returnValueHandlers.handleReturnValue(
				returnValue, getReturnValueType(returnValue), mavContainer, webRequest);	// #4
	}
	...
}

複製代碼

#1 處理請求,注意providedArgs參數是null
#2 設置響應碼
#3 處理異常場景
#4 處理業務方法返回結果

public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);	// #1
	...
	Object returnValue = doInvoke(args);	// #2
	...
	return returnValue;
}
複製代碼

#1 從http請求中組裝業務方法的參數
#2 調用業務方法

解析業務方法參數

private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
		Object... providedArgs) throws Exception {

	MethodParameter[] parameters = getMethodParameters();	// #1
	Object[] args = new Object[parameters.length];
	for (int i = 0; i < parameters.length; i++) {
		MethodParameter parameter = parameters[i];
		parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
		args[i] = resolveProvidedArgument(parameter, providedArgs);	// #2
		if (args[i] != null) {
			continue;
		}
		if (this.argumentResolvers.supportsParameter(parameter)) {
			try {
				args[i] = this.argumentResolvers.resolveArgument(
						parameter, mavContainer, request, this.dataBinderFactory);	// #3
				continue;
			}
			...
		}
		...
	}
	return args;
}
複製代碼

#1 獲取業務方法參數信息
#2 從providedArgs獲取對應的方法參數(providedArgs參數是null,這裏都是返回null)
#3 從http請求中解析方法參數

HandlerMethodArgumentResolverComposite#resolveArgument

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

	HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);	// #1
	if (resolver == null) {
		throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
	}
	return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
複製代碼

#1 根據參數的註解,從HandlerMethodArgumentResolverComposite#argumentResolvers選擇正確的HandlerMethodArgumentResolver處理參數。
RequestParamMethodArgumentResolver處理@RequestParam註解的參數
PathVariableMethodArgumentResolver處理@PathVariable註解的參數
RequestResponseBodyMethodProcessor處理@RequestBody註解的參數
注意:RequestResponseBodyMethodProcessor調用父類方法AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters方法,該方法會根據HttpReqeust的contentType,查找對應的HttpMessageConverter進行處理。

處理業務方法的結果

ServletInvocableHandlerMethod#invokeAndHandle方法#4步驟 -> HandlerMethodReturnValueHandlerComposite#handleReturnValue

public void handleReturnValue(Object returnValue, MethodParameter returnType,
		ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

	HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);	// #1
	if (handler == null) {
		throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
	}
	handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);	// #2
}
複製代碼

#1 根據業務方法註解,執行結果類型等信息,從returnValueHandlers從選擇一個HandlerMethodReturnValueHandler
#2 使用HandlerMethodReturnValueHandler處理結果。
RequestResponseBodyMethodProcessor#handleReturnValue負責處理@ResponseBody註解的方法結果,也是經過HttpMessageConverter轉化數據,並將轉化後的數據寫入到http響應中。

HandlerInterceptor

HandlerInterceptor也是咱們經常使用的功能。HandlerInterceptor相似於Servlet規範中的過濾器Filter,能夠對每一個request進行預處理和後處理。經過HandlerInterceptor,咱們能夠實現身份認證,日誌等功能。

DispatcherServlet#doDispatch方法調用了mappedHandler#applyPreHandle, mappedHandler#applyPostHandle,在processDispatchResult方法中會調用mappedHandler#triggerAfterCompletion。

異常處理

DispatcherServlet#processDispatchResult -> processHandlerException
若是拋出的異常是ModelAndViewDefiningException,獲取ModelAndView並渲染,不然使用HandlerExceptionResolver進行處理。
SpringMvc默認提供了三種HandlerExceptionResolver實現,
ExceptionHandlerExceptionResolver:查找Spring Context中@ControllerAdvice標註的類,並使用它們@ExceptionHandler標註的方法處理異常。
DefaultHandlerExceptionResolver:處理常見的異常,如業務方法不存放,http請求內容解析失敗等
ResponseStatusExceptionResolver:解析@ResponseStatus標註的異常類

DispatcherServlet會加載spring-webmvc.jar下DispatcherServlet.properties配置,獲取一些組件接口的默認實現類,HandlerExceptionResolver的默認實現類就在該文件中配置了。

咱們也能夠自定義HandlerExceptionResolver的實現,進行異常處理。

Spring Content層次

除了spring-mvc.xml的配置,咱們還能夠定義一個spring.xml,存放除了非WEB的bean以及配置,而後使用在web.xml中使用ContextLoaderListener加載該配置。
ContextLoaderListener#contextInitialized -> ContextLoader#initWebApplicationContext,這裏會構造一個RootSpringContext,而且添加到servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE屬性中。
FrameworkServlet#initWebApplicationContext也會構造一個WebSpringContext,並將servletContext的ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE指向的SpringContext做爲父Content。
注意:WebSpringContext可使用RootSpringContext的bean,但RootSpringContext不能使用WebSpringContext定義的bean。

SpringBoot中,複用了DispatcherServlet,HttpMessageConverter,RequestMappingHandlerAdapter,RequestMappingHandlerMapping等組件,主要的處理流程並無變化,後面也寫文章解析對應的內容。

若是您以爲本文不錯,歡迎關注個人微信公衆號,您的關注是我堅持的動力!

相關文章
相關標籤/搜索