Spring Web源码之执行体系一

  • Post author:
  • Post category:其他




前言

上篇博文,小编介绍了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的执行体系,对比映射体系执行还是相对比较复杂的。最后还得啃源码,再接再厉,加油!



版权声明:本文为a1032722788原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。