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就不一样了做了大量的工作,所以接下来小编把精力都放在他的身上。先看一下他的执行步骤:
- 找到适配器后,进行参数处理
- 参数处理器完成后校验HandlerMethod处理
- HandlerMethod通过反射,由目标对象执行方法
- 返回结果进行结果集处理
题外话:小编突然想到好多源码框架的逻辑或设计有点相似,像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的结构上面小编也有简单提起),这里会回顾一下上篇映射体系的一部分内容,先看整体结构:
这里小编在上篇博文中忘记了映射器的初始化过程,这里补上:
- 从IOC容器中取出带注解@Controller和@RequestMapping的bean
- 取出含有@RequestMapping的方法
- 基于@RequestMapping的bean和方法封装完Mapping
- 创建HandlerMethod
- 存储映射
相关源码:
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源码之执行体系一的主要内容,如果未能解决你的问题,请参考以下文章