Spring Web源码之执行体系一

Posted 木兮君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Web源码之执行体系一相关的知识,希望对你有一定的参考价值。

前言

上篇博文,小编介绍了spring mvc的映射体系,可以简单的理解为从url找到handler的过程,那找到handler以后那就需要开始执行逻辑了,今天小编就分享spring mvc的执行体系,只不过执行体系比映射体系复杂的多,一篇博文搞不定了,小编会分为几篇,希望自己能讲清楚。一如既往的废话不多说,进入正题。

执行体系结构

首先我们来看看执行体系的整体结构(从设计者的角度把握全局)。


这里执行体系结构包含了适配器与执行器,映射体系中其实找到的时候咱们的handler,但是真正执行需要通过适配器。而适配器中除了第一个RequestMappingHandlerAdapter(他还有一个抽象父类AbstractHandlerMethodAdapter)真正做了适配外,其他的三个HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter是直接交给对应的Handler去做处理。简单看下其中一个的源码:

public class SimpleServletHandlerAdapter implements HandlerAdapter 

	@Override
	//适配 相当于是否是Servlet
	public boolean supports(Object handler) 
		return (handler instanceof Servlet);
	

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception 
		//执行时直接强转Servlet,交由他执行
		((Servlet) handler).service(request, response);
		return null;
	

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) 
		return -1;
	


而RequestMappingHandlerAdapter就不一样了做了大量的工作,所以接下来小编把精力都放在他的身上。先看一下他的执行步骤:

  1. 找到适配器后,进行参数处理
  2. 参数处理器完成后校验HandlerMethod处理
  3. HandlerMethod通过反射,由目标对象执行方法
  4. 返回结果进行结果集处理

题外话:小编突然想到好多源码框架的逻辑或设计有点相似,像mybatis的执行器,参数处理然后是结果集处理。

这里小编整理了一张图:

小编继续带大家看一下四个组件类的结构以及一些重要的子类,方法,参数等等:


其实适配器包含了参数解析以及结果集处理器。接下来小编再用图串联一下上面的三个重要组件:


源码阅读
这里小编所有的整理都是根据源码而来,还是要稍微证明一下的:
HandlerMethodArgumentResolverComposite :

@Override
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver 

	
	private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();

	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception 
		//解析先遍历得到解析器,如果没有则抛异常,有则解析
		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) 
			throw new IllegalArgumentException(
					"Unsupported parameter type [" + parameter.getParameterType().getName() + "]." +
							" supportsParameter should be called first.");
		
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	

	/**
	 * Find a registered @link HandlerMethodArgumentResolver that supports
	 * the given method parameter.
	 */
	@Nullable
	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;
	

HandlerMethodReturnValueHandlerComposite :

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler 


	private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
	
	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception 

		HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
		if (handler == null) 
			throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
		
		handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
	

	@Nullable
	private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) 
		boolean isAsyncValue = isAsyncReturnValue(value, returnType);
		for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) 
			if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) 
				continue;
			
			if (handler.supportsReturnType(returnType)) 
				return handler;
			
		
		return null;
	

看到这儿,有没有小伙伴想到一个问题,那参数解析器以及结果集的处理器在哪儿初始化进去的。那小编带大家看一下代码:
RequestMappingHandlerAdapter,因为实现了InitializingBean接口所以实现了他唯一的方法afterPropertiesSet,这里就是初始化的内容

@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);
		
		//@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);
		
	

加进去也很简单随便看一个就可以了:

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
		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;
	

上面小编说了四个重要组件,串联的时候小编是否少了一个执行器,那紧接着继续看下执行器的结构(Hanlder的结构上面小编也有简单提起),这里会回顾一下上篇映射体系的一部分内容,先看整体结构:

这里小编在上篇博文中忘记了映射器的初始化过程,这里补上:

  1. 从IOC容器中取出带注解@Controller和@RequestMapping的bean
  2. 取出含有@RequestMapping的方法
  3. 基于@RequestMapping的bean和方法封装完Mapping
  4. 创建HandlerMethod
  5. 存储映射

相关源码:

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#initHandlerMethods
方法触发的话同样实现了InitializingBean这个类。

@Override
	public void afterPropertiesSet() 
		initHandlerMethods();
	

	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #getCandidateBeanNames()
	 * @see #processCandidateBean
	 * @see #handlerMethodsInitialized
	 */
	protected void initHandlerMethods() 
		for (String beanName : getCandidateBeanNames()) 
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) 
				processCandidateBean(beanName);
			
		
		handlerMethodsInitialized(getHandlerMethods());
	

这里小编就不带大家继续看源码了,有兴趣的小伙伴往里面看,小编只将里面的流程图给大家看下:

大家可以根据以下测试代码来调试上面的流程:

public class RequestMappingInitTest 
    private StaticWebApplicationContext webApplicationContext;
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Before
    public void init() 
        webApplicationContext = new StaticWebApplicationContext();
        webApplicationContext.registerBean("initMappingTest", InitMappingTest.class);
        requestMappingHandlerMapping = new RequestMappingHandlerMapping();
        requestMappingHandlerMapping.setApplicationContext(webApplicationContext);
    

    @Test
    public void mappingHandler() 
        requestMappingHandlerMapping.afterPropertiesSet();
    

    @Controller
    public class InitMappingTest 
        @GetMapping("/getUser")
        public ModelAndView getUser() 
            ModelAndView modelAndView = new ModelAndView("/userView");
            User user = new User(1L, "simon");
            modelAndView.addObject("user", user);
            return modelAndView;
        
    

看完了整体的体系以及组件的初始化后,咱们再来看里面的执行流程。

主体流程

首先看执行流程:

还是一样小编上一个调试代码:让大家自己调试,不贴出很多源码了:

public class RequestMappingInitTest 
    private StaticWebApplicationContext webApplicationContext;
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
    private HandlerMethod handlerMethod;

    @Before
    public void init() throws NoSuchMethodException 
        webApplicationContext = new StaticWebApplicationContext();
        webApplicationContext.registerBean("initMappingTest", InitMappingTest.class);
        requestMappingHandlerMapping = new RequestMappingHandlerMapping();
        requestMappingHandlerMapping.setApplicationContext(webApplicationContext);
        requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
        requestMappingHandlerAdapter.setApplicationContext(webApplicationContext);
        //记得初始化
        requestMappingHandlerAdapter.afterPropertiesSet();
        handlerMethod = new HandlerMethod(new InitMappingTest(), "getUser", null);


    

    @Test
    public void executeHandler() throws Exception 
        HttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(HttpMethod.GET.name(), "/getUser");
        HttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
//        映射查找过程
//        requestMappingHandlerMapping.getHandler(mockHttpServletRequest).getHandler()
//		当然下面其实调用handler方法更为合适,只不过小编只关心最核心的那部分
        ModelAndView modelAndView = requestMappingHandlerAdapter.invokeHandlerMethod(mockHttpServletRequest, mockHttpServletResponse, handlerMethod);
        System.out.println(modelAndView);
    

    @Controller
    public class InitMappingTest 
        @GetMapping("/getUser")
        public ModelAndView getUser() 
            ModelAndView modelAndView = new ModelAndView("/userView");
            User user = new User(1L, "simon");
            modelAndView.addObject("user", user);
            return modelAndView;
        
    

大家不要过度关注里面的细节,看整体流程,先明白整体的调用就可以了。之后小编还会为大家带来细节的分享。
具体源码类:

//初始化环境
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
//执行请求
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
//解析参数
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
//反射调用目标对象
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
//处理结果集(这里会将modelAndView封装到ModelAndViewContainer容器中)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
//封装结果
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getModelAndView

ModelAndViewContainer说明:为什么需要ModelAndViewContainer,因为有时候我们返回的不需要视图有可能是重定向,他就很好的包装了这一些需要返回的内容

总结

今天小编只分析了一部分执行体系,希望大家可以理解明白,接下来还是会继续spring mvc的执行体系,对比映射体系执行还是相对比较复杂的。最后还得啃源码,再接再厉,加油!

以上是关于Spring Web源码之执行体系一的主要内容,如果未能解决你的问题,请参考以下文章

Spring Web源码之执行体系一

Spring Web源码之执行体系一

Spring Web源码之映射体系

Spring Web源码之映射体系

Spring Web源码之映射体系

Spring Web源码之映射体系