Spring源码解析 -- SpringMvc原理

491 阅读7分钟

Spring源码解析 -- 读取bean元数据
spring源码解析 -- 构造bean
spring源码解析 -- 注入属性
spring源码解析 -- Spring Context
Spring源码解析 -- AOP原理(1)
Spring源码解析 -- AOP原理(2)
Spring源码解析 -- SpringMvc原理

源码分析基于spring 4.3.x
本文通过阅读Spring MVC的源码,解析Spring MVC实现原理。本文不会深入SpringMvc的细节,关注于分析SpringMvc的各个核心组件以及主要逻辑,以便大家深入SpringMvc以及排查问题。

关于阅读源码的思路,可参考 -- 如何阅读java源码

使用Spring MVC时,我们常编写一个spring-mvc.xml,xml中添加<mvc:annotation-driven/>标签,这个标签是由MvcNamespaceHandler处理。
MvcNamespaceHandler#parse -> AnnotationDrivenBeanDefinitionParser#parse

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前后做一些额外处理

RequestMappingHandlerMapping维护了URL,@RequestMapping注解以及业务方法(就是@RequestMapping标注的方法)之间的映射关系,父类AbstractHandlerMethodMapping实现了InitializingBean接口,AbstractHandlerMethodMapping#afterPropertiesSet -> initHandlerMethods

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的关键组件类。

DispatcherServlet#initStrategies方法将加载Spring Context的SpringMvc组件类,如MultipartResolver,HandlerMapping,设置为自己的属性。
如initHandlerMappings方法获取AnnotationDrivenBeanDefinitionParser中构造的RequestMappingHandlerMapping,作为自己的属性handlerMappings。

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等组件,主要的处理流程并没有变化,后面也写文章解析对应的内容。

如果您觉得本文不错,欢迎关注我的微信公众号,您的关注是我坚持的动力!