1.父子容器的创建(一般只创建DispatcherServlet中的子容器)
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring.xml</param-value>
</context-param>
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
父容器的创建
ContextLoaderListener的作用是用来创建一个Spring容器,就是我们说的SpringMVC父子容器中的父容器,执行流程为:
- Tomcat启动,解析web.xml时
- 发现定义了一个ContextLoaderListener,Tomcat就会执行该listener中的contextInitialized()方法,该方法就会去创建要给Spring容器
- 从ServletContext中获取contextClass参数值,该参数表示所要创建的Spring容器的类型,可以在web.xml中通过<context-param>来进行配置
- 如果没有配置该参数,那么则会从ContextLoader.properties文件中读取org.springframework.web.context.WebApplicationContext配置项的值,SpringMVC默认提供了一个ContextLoader.properties文件,内容为org.springframework.web.context.support.XmlWebApplicationContext
- 所以XmlWebApplicationContext就是要创建的Spring容器类型
- 确定好类型后,就用反射调用无参构造方法创建出来一个XmlWebApplicationContext对象
- 然后继续从ServletContext中获取contextConfigLocation参数的值,也就是一个spring配置文件的路径
- 把spring配置文件路径设置给Spring容器,然后调用refresh(),从而启动Spring容器,从而解析spring配置文件,从而扫描生成Bean对象等
- 这样Spring容器就创建出来了
- 有了Spring容器后,就会把XmlWebApplicationContext对象作为attribute设置到ServletContext中去,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
- 把Spring容器存到ServletContext中的原因,是为了给Servlet创建出来的子容器来作为父容器的
子容器的创建
Tomcat启动过程中,执行完ContextLoaderListener的contextInitialized()之后,就会创建DispatcherServlet了,web.xml中定义DispatcherServlet时,load-on-startup为1,表示在Tomcat启动过程中要把这个DispatcherServlet创建并初始化出来,而这个过程是比较费时间的,所以要把load-on-startup设置为1,如果不为1,会在servlet接收到请求时才来创建和初始化,这样会导致请求处理比较慢。
- Tomcat启动,解析web.xml时
- 创建DispatcherServlet对象
- 调用DispatcherServlet的init()
- 从而调用initServletBean()
- 从而调用initWebApplicationContext(),这个方法也会去创建一个Spring容器(就是子容器)
- initWebApplicationContext()执行过程中,会先从ServletContext拿出ContextLoaderListener所创建的Spring容器(父容器),记为rootContext
- 然后读取contextClass参数值,可以在servlet中的<init-param>标签来定义想要创建的Spring容器类型,默认为XmlWebApplicationContext
- 然后创建一个Spring容器对象,也就是子容器
- 将rootContext作为parent设置给子容器(父子关系的绑定)
- 然后读取contextConfigLocation参数值,得到所配置的Spring配置文件路径
- 然后就是调用Spring容器的refresh()方法
- 从而完成了子容器的创建
2.DispatcherServlet初始化逻辑
DispatcherServlet#init()实际是父类HttpServletBean#init。
- FrameworkServlet#initServletBean
- FrameworkServlet#initWebApplicationContext
- FrameworkServlet#createWebApplicationContext
- 默认创建的是XmlWebApplicationContext
- 给容器设置ServletContext、ServletConfig、Namespace
- 给容器添加ContextRefreshListener监听器,监听ContextRefreshedEvent事件
- 执行ApplicationContextInitializer#initialize
- wac.refresh()容器刷新,在刷新的最后finishRefresh()中会发布ContextRefreshedEvent事件,此时会调用监听器ContextRefreshListener#onApplicationEvent
- ContextRefreshListener#onApplicationEvent调用FrameworkServlet#onApplicationEvent,调用至DispatcherServlet#onRefresh,然后调用至DispatcherServlet#initStrategies
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
2.1 四种Handler
在SpringMVC中有四种Handler:
- 实现了Controller接口的Bean对象
- 实现了HttpRequestHandler接口的Bean对象
- 添加了@RequestMapping注解的方法
- 一个HandlerFunction对象(RouterFunction)
2.2 HandlerMappings初始化
DispatcherServlet#initHandlerMappings
- DispatcherServlet#getDefaultStrategies
从DispatcherServlet.properties中获取key为HandlerMapping的类。
可得HandlerMapping:
- BeanNameUrlHandlerMapping:负责Controller接口和HttpRequestHandler接口
- RequestMappingHandlerMapping:负责@RequestMapping的方法
- RouterFunctionMapping:负责RouterFunction以及其中的HandlerFunction
DispatcherServlet#createDefaultStrategy
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);
}
这三个对象是通过createBean()创建的,所以有Bean的生命周期动作,包括调用初始化方法。
比如RequestMappingHandlerMapping#afterPropertiesSet负责处理@Controller和@RequestMapping的方法
解析出来有两个map:pathLookup<path, mapping注解信息>以及registry<mapping注解, method>
RequestMappingHandlerMapping#afterPropertiesSet
- 1)找出容器中定义的所有的beanName
- 2)根据beanName找出beanType
- 3)判断beanType上是否有@Controller注解或@RequestMapping注解,如果有那么就表示这个Bean对象是一个Handler
- 4)如果是一个Handler,就通过反射找出加了@RequestMapping注解的Method,并解析@RequestMapping注解上定义的参数信息,得到一个对应的RequestMappingInfo对象,然后结合beanType上@RequestMapping注解所定义的path,以及当前Method上@RequestMapping注解所定义的path,进行整合,则得到了当前这个Method所对应的访问路径,并设置到RequestMappingInfo对象中去
- 5)所以,一个RequestMappingInfo对象就对应了一个加了@RequestMapping注解的Method,并且请求返回路径也记录在了RequestMappingInfo对象中
- 6)把当前Handler,也就是beanType中的所有RequestMappingInfo都找到后,就会存到MappingRegistry对象中
- 7)在存到MappingRegistry对象过程中,会像把Handler,也就是beanType,以及Method,生成一个HandlerMethod对象,其实就是表示一个方法
- 8)然后获取RequestMappingInfo对象中的path
- 9)把path和HandlerMethod对象存在一个Map中,属性叫做pathLookup
- 10)这样在处理请求时,就可以同请求路径找到HandlerMethod,然后找到Method,然后执行了
RequestMappingHandlerMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#initHandlerMethods
- AbstractHandlerMethodMapping.MappingRegistry#register
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
2.3 HandlerAdapter初始化
SpringMVC采用适配模式,把不同种类的Handler适配成一个HandlerAdapter,后续再执行HandlerAdapter的handle()方法就能执行不同种类Hanlder对应的方法。
DispatcherServlet#initHandlerAdapters从DispatcherServlet.properties中获得的适配器:
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- RequestMappingHandlerAdapter
- HandlerFunctionAdapter
同理适配器对象也是通过createBean()创建的,所以有Bean的生命周期动作,包括调用初始化方法。
重点看一下RequestMappingHandlerAdapter#afterPropertiesSet
- 1)从Spring容器中找到加了@ControllerAdvice的Bean对象
1-1)解析出Bean对象中加了@ModelAttribute注解的Method对象,并存在modelAttributeAdviceCache这个Map中
1-2)解析出Bean对象中加了@InitBinder注解的Method对象,并存在initBinderAdviceCache这个Map中
1-3)如果Bean对象实现了RequestBodyAdvice接口或者ResponseBodyAdvice接口,那么就把这个Bean对象记录在requestResponseBodyAdvice集合中 - 2)从Spring容器中获取用户定义的HandlerMethodArgumentResolver,以及SpringMVC默认提供的,整合为一个HandlerMethodArgumentResolverComposite对象,HandlerMethodArgumentResolver是用来解析方法参数的
- 3)从Spring容器中获取用户定义的HandlerMethodReturnValueHandler,以及SpringMVC默认提供的,整合为一个HandlerMethodReturnValueHandlerComposite对象,HandlerMethodReturnValueHandler是用来解析方法返回值的
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);
}//数据绑定器参数解析器
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<>(30);
// 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());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
默认的数据绑定解析器
private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);
// 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 ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
return resolvers;
}
默认的返回值处理器
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ServletModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types [可扩展]
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ServletModelAttributeMethodProcessor(true));
}
return handlers;
}
3.请求处理逻辑
DispatcherServlet父类HttpServlet#service
- FrameworkServlet#doGet/ doPost
- DispatcherServlet#doService
- DispatcherServlet#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);
multipartRequestParsed = (processedRequest != request);
// 进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 找到最合适的HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler. HTTP缓存相关
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果mv有 视图没有,给你设置默认视图
applyDefaultViewName(processedRequest, mv);
//后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
DispatcherServlet#doDispatch:
- 1)会从三个HandlerMapping(this.handlerMappings)根据request查找HandlerExecutionChain
- 2)拿到对应的Handler后,会转换为HandlerAdapter。采用的适配模式,把不同种类的Handler适配成一个HandlerAdapter,后续再执行HandlerAdapter的handle()方法就能执行不同种类Hanlder对应的方法。(开闭原则,从DispatcherServlet.properties中读取配置)
- 3)mappedHandler#applyPreHandle 拦截器前置方法,返回false,会执行拦截器afterCompletion方法然后直接结束返回,后续步骤不会执行。
- 4)HandlerAdapter#handle,RequestMappingHandlerAdapter#handleInternal
- 4-1)ServletInvocableHandlerMethod#invokeAndHandle,InvocableHandlerMethod#invokeForRequest
A)获取参数InvocableHandlerMethod#getMethodArgumentValues(RequestMappingHandlerAdapter#getDefaultArgumentResolvers中定义了很多默认的参数解析器)
A-1)RequestParamMethodArgumentResolver#resolveName解析@RequestParam("key")注解,从请求request里面获取到注解key对应的值,然后传给方法的参数。并且该参数解析器也能解析不加注解的情况:基本类型以及Date、Enum、Number、CharSequence(比如String)等。(String -> User,可以定义类型转换器PropertyEditorSupport,然后@InitBinder,注册User -> PropertyEditorSupport)。也可以解析MultipartFile类型的参数,@RequestPart只拿表单里面的参数。
A-2)ServletModelAttributeMethodProcessor#resolveArgument是个兜底的参数解析器,可以解析不是简单类型的参数。首先就是根据参数类型拿到无参构造方法,然后当做入参,所以这个参数解析器跟请求request无关,不会从request中拿任何值。其次,如果只有一个有参构造方法,会根据参数名去request中拿值,然后传给有参构造方法。
B)doInvoke(args)调用我们定义的目标方法 - 4-2)this.returnValueHandlers.handleReturnValue()返回值处理器处理(RequestMappingHandlerAdapter#getDefaultReturnValueHandlers中定义了很多默认的返回值解析器)
A)(在RequestMappingHandlerAdapter中)默认有四个messageConverter(也要结合浏览器那边可以支持的格式:Accept:text/hmtl等等):ByteArrayHttpMessageConverter、StringHttpMessageConverter、SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter。
B)ViewNameMethodReturnValueHandler,处理的就是返回值为字符串的请求(无@ResponseBody),会调用viewResolvers视图解析器解析视图(JSP生成视图实质是转发,Freemarker是真正的渲染视图)
- 4-1)ServletInvocableHandlerMethod#invokeAndHandle,InvocableHandlerMethod#invokeForRequest
- 5)mappedHandler.applyPostHandle 拦截器后置方法
- 6)processDispatchResult()渲染视图,执行拦截器afterCompletion方法
3.1 查找HandlerMapping
AbstractHandlerMapping会负责调用子类的getHandlerInternal(HttpServletRequest request)方法从而找到请求对应的Handler,然后AbstractHandlerMapping负责将Handler和应用中所配置的HandlerInterceptor整合成为一个HandlerExecutionChain对象。
查找优先级是按照配置的顺序:
- BeanNameUrlHandlerMapping
- RequestMappingHandlerMapping
- RouterFunctionMapping
3.2 转换为HandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
适配逻辑:
- HttpRequestHandlerAdapter
- SimpleControllerHandlerAdapter
- RequestMappingHandlerAdapter
- HandlerFunctionAdapter
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
3.3 HandlerAdapter#handle
HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter
和HandlerFunctionAdapter三个接收的直接就是Requeset对象,不用SpringMVC做额外的解析,所以比较简单。
比较复杂的是RequestMappingHandlerAdapter,它执行的是加了@RequestMapping的方法,而这种方法的写法可以是多种多样,SpringMVC需要根据方法的定义去解析Request对象,从请求中获取出对应的数据然后传递给方法,并执行。
RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查当前请求的method是否为支持的method(默认Null,可通过继承AbstractController设置supportedMethods)
// 检查当前请求是否必须session (默认false,可通过继承AbstractController设置requireSession)
checkRequest(request);
/**
* 判断当前是否需要支持在同一个session中只能线性地处理请求
* 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下,
* 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false
*/
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// *如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
// 这里SessionAttribute主要是通过@SessionAttribute注解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
// 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
// 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
// Cache的过期时间为-1,即立即失效
prepareResponse(response);
}
}
return mav;
}
RequestMappingHandlerAdapter#invokeHandlerMethod
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 把我们的请求req resp包装成 ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
// 配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
// 这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 让invocableMethod拥有参数解析能力
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 让invocableMethod拥有返回值处理能力
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 让invocableMethod拥有InitBinder解析能力
invocableMethod.setDataBinderFactory(binderFactory);
// 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndView处理容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将request的Attribute复制一份到ModelMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 重定向的时候,忽略model中的数据 默认false
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
// handler的返回值是否为WebAsyncTask或DeferredResult,如果是这两种中的一种,
// 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
// 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
// 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,提高吞吐。
// 只有待目标任务完成之后才会回来将该异步任务的结果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 封装异步任务的线程池、request、interceptors到WebAsyncManager中
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
// 这里就是用于判断当前请求是否有异步任务结果的,如果存在,则对异步任务结果进行封装
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
核心处理在ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*真正的调用我们的目标对象 很重要 很重要*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置相关的返回状态
setResponseStatus(webRequest);
// 如果请求处理完成,则设置requestHandled属性
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
// 如果请求失败,但是有错误原因,那么也会设置requestHandled属性
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
// 如果支持,则使用该handler处理该返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//*获取我们目标方法入参的值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
//真的的调用我们的目标方法
return doInvoke(args);
}
3.3.1 @RequestMapping方法参数解析
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取目标方法参数的描述数组对象
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
//用来初始化我们对应参数名称的参数值得数组
Object[] args = new Object[parameters.length];
//循环我们得参数名数组
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//为我们得MethodParameter设置参数名称探测器对象
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs); // providedArgs
if (args[i] != null) {
continue;
}
// * 获取所有的参数解析器,然后筛选出合适的解析器
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 通过上面筛选的 参数解析器来解析我们的参数
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
一个HttpServletRequest通常有:
- request parameter,没有加任何注解test(String username),从request parameter中获取key为入参名字的value;test(@RequestParam("uname") String username) 从request parameter中获取key为uname的value;
- request attribute,test(@RequestAttribute String username)表示要从request attribute中获取key为username的value;
- request session,test(@SessionAttribute String username)表示要从request session中获取key为username的value
- reqeust header,test(@RequestHeader String username)表示要从request header中获取key为username的value
- reqeust body,test(@RequestBody String username)表示获取整个请求体
解析方法参通过HandlerMethodArgumentResolver来实现的,比如:
- RequestParamMethodArgumentResolver:负责处理@RequestParam
- RequestHeaderMethodArgumentResolver:负责处理@RequestHeader
- SessionAttributeMethodArgumentResolver:负责处理@SessionAttribute
- RequestAttributeMethodArgumentResolver:负责处理@RequestAttribute
- RequestResponseBodyMethodProcessor:负责处理@RequestBody
- PathVariableMethodArgumentResolver,用来解析加了@PathVariable注解的参数
- RequestHeaderMethodArgumentResolver,用来解析加了@RequestHeader注解的参数
在判断某个参数该由哪个HandlerMethodArgumentResolver处理时,是很粗暴,HandlerMethodArgumentResolverComposite#getArgumentResolver:
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
就是遍历所有的HandlerMethodArgumentResolver,哪个能支持处理当前这个参数就由哪个处理。
类型转换
从请求中获取的值可能很多时候都是字符串,那如果参数类型不是String,该怎么办呢?这就需要进行类型转换了,比如代码是这么写的:
@RequestMapping(method = RequestMethod.GET, path = "/test")
@ResponseBody
public String test(@RequestParam User user) {
System.out.println(user.getName());
return "hello zhouyu";
}
请求:http://localhost:8080/tuling-web/app/test?user=wz
那么SpringMVC就需要将字符串zhouyu转换成为User对象,这就需要我们自定义类型转换器了。
public class StringToUserEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
User user = new User();
user.setName(text);
this.setValue(user);
}
}
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(User.class, new StringToUserEditor());
}
Spring默认提供的Converter:org.springframework.core.convert.support.DefaultConversionService#addCollectionConverters
MultipartFile解析
当接收到一个请求后:
- SpringMVC利用MultipartResolver来判断当前请求是不是一个multipart/form-data请求
- 如果是会把这个请求封装为StandardMultipartHttpServletRequest对象
- 并且获取请求中所有的Part,并且遍历每个Part
- 判断Part是文件还是文本
- 如果是文件,会把Part封装为一个StandardMultipartFile对象(实现了MultipartFile接口),并且会把StandardMultipartFile对象添加到multipartFiles中
- 如果是文本,会把Part的名字添加到multipartParameterNames中
- 然后在解析某个参数时
- 如果参数类型是MultipartFile,会根据参数名字从multipartFiles中获取出StandardMultipartFile对象,最终把这个对象传给方法
3.3.2 @RequestMapping方法返回值解析
方法返回值,也会分为不同的情况。比如有没有加@ResponseBody注解,如果方法返回一个String:
- 加了@ResponseBody注解:表示直接将这个String返回给浏览器
- 没有加@ResponseBody注解:表示应该根据这个String找到对应的页面,把页面返回给浏览器
在SpringMVC中,会利用HandlerMethodReturnValueHandler来处理返回值:
- RequestResponseBodyMethodProcessor:处理加了@ResponseBody注解的情况
- ViewNameMethodReturnValueHandler:处理没有加@ResponseBody注解并且返回值类型为String的情况
- ModelMethodProcessor:处理返回值是Model类型的情况
- ModelAndViewMethodReturnValueHandler,处理的就是返回值类为ModelAndView的情况.
这里着重分析RequestResponseBodyMethodProcessor,因为它会处理加了@ResponseBody注解的情况,也是目前我们用得最多的情况。
类型转换
RequestResponseBodyMethodProcessor相当于会把方法返回的对象直接响应给浏览器,如果返回的是一个字符串,那么好说,直接把字符串响应给浏览器,那如果返回的是一个Map呢?是一个User对象呢?该怎么把这些复杂对象响应给浏览器呢?
方法返回的是User对象,那么怎么把这个User对象返回给浏览器来展示呢?那得看当前请求设置的Accept请求头,比如我用Chrome浏览器发送请求,默认给我设置的就是:Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
表示当前这个请求接收的内容格式,比如html格式、xml格式、各种图片格式等等。
如果我们的方法返回的是一个字符串,那么就对应html格式,就没问题,而如果我们不是返回的字符串,那我们就转成字符串,通常就是JSON格式的字符串。
处理这块,SpringMVC会利用HttpMessageConverter来处理,比如默认情况下,SpringMVC会有4个HttpMessageConverter:
- ByteArrayHttpMessageConverter:处理返回值为字节数组的情况,把字节数组返回给浏览器
- StringHttpMessageConverter:处理返回值为字符串的情况,把字符串按指定的编码序列号后返回给浏览器
- SourceHttpMessageConverter:处理返回值为XML对象的情况,比如把DOMSource对象返回给浏览器
- AllEncompassingFormHttpMessageConverter:处理返回值为MultiValueMap对象的情况
StringHttpMessageConverter#writeInternal
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
HttpHeaders headers = outputMessage.getHeaders();
if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {
headers.setAcceptCharset(getAcceptedCharsets());
}
Charset charset = getContentTypeCharset(headers.getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
先看有没有设置Content-Type,如果没有设置则取默认的,默认为ISO-8859-1,所以默认情况下返回中文会乱码。需要指定为UTF-8才能解决乱码问题。
以上四个Converter是不能处理Map对象或User对象的,所以如果返回的是Map或User对象,那么得单独配置一个Converter,比如MappingJackson2HttpMessageConverter,这个Converter比较强大,能把String、Map、User对象等等都能转化成JSON格式。
@Configuration
@EnableWebMvc
public class AppConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setDefaultCharset(StandardCharsets.UTF_8);
converters.add(messageConverter);
}
}
网友评论