DispatcherServlet 调用分析

警告
本文最后更新于 2020-12-17,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


DispatcherServlet.doDispatch()中,具体业务方法是通过HandlerAdapter.handle()来调用的,本文性详细列举handle()的实现流程。

方法调用流程:
ha.handle() -> handleInternal() -> invokeHandlerMethod() -> invokeAndHandle() -> invokeForRequest() -> getMethodArgumentValues() -> resolveArgument() -> getArgumentResolver() -> resolveArgument() -> doInvoke() -> invoke() -> handleReturnValue()

按照以上方法调用流程,来看源码。

invokeHandlerMethod() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		try {
			// 获取数据绑定工厂  @InitBinder注解支持,没太多用
			WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
			// Model工厂,收集了@ModelAttribute注解的方法
			ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

			// 可调用的方法对象
			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();
			mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
			// 调用有@ModelAttribute注解的方法。每次请求都会调用有@ModelAttribute注解的方法
			// 把@ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的map中了
			modelFactory.initModel(webRequest, mavContainer, invocableMethod);
			mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
            // 省略部分代码...

			// TODO 重点:Controller方法调用
			invocableMethod.invokeAndHandle(webRequest, mavContainer);
			if (asyncManager.isConcurrentHandlingStarted()) {
				return null;
			}

			return getModelAndView(mavContainer, modelFactory, webRequest);
		}
		finally {
			webRequest.requestCompleted();
		}
	}
}
  1. 首先封装 ServletWebRequest,其实句式对 request、response 实例的封装。
  2. 获取绑定工厂 WebDataBinderFactory,收集带有 @InitBinder 注解的方法,这个功能不常用。
  3. 获取 ModelFactory。收集类中没有 @RequestMapping 且有 @ModelAttribute 注解的方法,收集完成后封装到 List 中,再封装为 ModelFactory。
  4. 创建 ServletInvocableHandlerMethod,其实就是对handlerMethod的封装。
  5. 设置参数解析器,将 RequestMappingHandlerAdapter 中的 argumentResolvers,赋值给 ServletInvocableHandlerMethod。
  6. 设置返回值处理器,步骤同上。
  7. 设置绑定工厂,就是吧步骤2中获取的对象,赋值到 ServletInvocableHandlerMethod。
  8. 设置参数名称解析类。同步骤6。
  9. 调用有@ModelAttribute注解的方法。每次请求都会调用有@ModelAttribute注解的方法,把@ModelAttribute注解的方法的返回值存储到 ModelAndViewContainer对象的map中了。这也是为什么普通的接口可以引用类中 @ModelAttribute 方法的返回值作为参数,@ModelAttribute 注解的方法总是会优先执行。
  10. 具体业务方法的调用 invokeAndHandle()。

在执行业务方法调用时,会调用至 invokeForRequest()。

invokeForRequest() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class InvocableHandlerMethod extends HandlerMethod {
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
		// TODO 重点:获取参数数组
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		// TODO 方法的反射执行
		return doInvoke(args);
	}
}

共分为两步:

  1. 获取参数数组。
  2. 反射调用业务方法。

getMethodArgumentValues() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class InvocableHandlerMethod extends HandlerMethod {

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

		if (ObjectUtils.isEmpty(getMethodParameters())) {
			return EMPTY_ARGS;
		}
		// 入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
		MethodParameter[] parameters = getMethodParameters();
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			// 设置参数名称解析器
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			// TODO 典型的策略模式,根据parameter能否找到对应参数的处理类,能找到就返回true
			//  这里遍历了所有的参数解析器,如果没有一个解析器支持此参数的解析,则抛出异常。
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
            // TODO 重点:具体参数值解析过程,策略模式
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			// 省略部分代码...
		}
		return args;
	}
}

resolveArgument() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
		// 根据参数获取对应参数的解析类,这里使用了策略模式,且使用了ConcurrentHashMap缓存
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
	    // 省略非关键代码...
		// 策略模式去调用具体参数解析类
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {

	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		// 从缓存中获取解析器
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			// 策略模式:循环所有的参数解析器,判断是否支持解析当前参数,如果支持,就将解析器放入缓存。
			for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
				if (methodArgumentResolver.supportsParameter(parameter)) {
					result = methodArgumentResolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
}

这里是典型策略模式的运用,HandlerMethodArgumentResolverComposite 中封装了容器中所有的 HandlerMethodArgumentResolver 实例, 获取到支持方法当前参数的 HandlerMethodArgumentResolver,调用 resolveArgument() 进行参数的解析。

doInvoke()源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class InvocableHandlerMethod extends HandlerMethod {

    protected Object doInvoke(Object... args) throws Exception {
		// 将方法设置为允许访问,反射调用方法执行。
		// 注意:方法的反射执行需要对象实例和参数列表。在handlerMethod封装时,并没有封装bean实例。这个bean是哪儿来的?
		ReflectionUtils.makeAccessible(getBridgedMethod());
        return getBridgedMethod().invoke(getBean(), args);
		// 省略非关键代码...
	}
}

这里通过方法反射调用 invoke(),对具体的业务方法进行调用。


而这里的 getBean(),仅仅返回了 this.bean。这个bean是在前面的流程 getHandler() 中进行了实例化的。

此处 getBean() 中返回的 bean,实例化的节点流程如下:

getHandler() -> getHandlerInternal() -> handlerMethod.createWithResolvedBean()

在 createWithResolvedBean() 中,调用 beanFactory.getBean() 获取了具体的实例对象。

createWithResolvedBean() 源码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class HandlerMethod {
    public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		// 最初这个 bean 成员变量,只是设置了一个 beanName,因此它是字符串类型的,执行到这里会调用 getBean(),将真正的实例设置到这里。
		// 如果已经设置过了,bean就不是字符串类型了,就不会进这个 if 语句。
		if (this.bean instanceof String) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			String beanName = (String) this.bean;
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}
}