SpringMVC源码系列文章
目录
- 一、SpringMVC全注解配置
- 二、SpringServletContainerInitializer
- 三、自定义配置类的加载
- 四、@EnableWebMvc解析
- 总结
一、SpringMVC全注解配置
1、pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xc.mvc</groupId> <artifactId>springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <!-- SpringMVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.27</version> </dependency> <!-- ServletAPI --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- Jackson --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <!--配置Maven中Java的编译版本 --> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
2、web容器初始化类(代替web.xml)
/** * web工程的初始化类,用来代替web.xml * 以下配置的都是以前在web.xml中配置的 */ public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { /** * Spring的配置,目前不涉及Spring,这里设置为空 */ @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[0]; } /** * SpringMVC的配置 */ @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[]{SpringMVCConfig.class}; } /** * 用于配置DispatcherServlet的映射路径 */ @Override protected String[] getServletMappings() { return new String[]{"/"}; } /** * 注册过滤器 */ @Override protected Filter[] getServletFilters() { // 字符编码过滤器 CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); characterEncodingFilter.setForceEncoding(true); // HiddenHttpMethodFilter 用于支持 PUT、DELETE 等 HTTP 请求 HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter}; } }
3、SpringMVC配置类(代替springmvc.xml)
// 将当前类标识为一个配置类 @Configuration // 仅仅扫描@Controller、@RestController @ComponentScan( value = "com.xc", includeFilters = {@ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class, RestController.class} )}, // 默认扫描 @Component @Repository, @Service, or @Controller useDefaultFilters = false ) // mvc注解驱动 @EnableWebMvc public class SpringMVCConfig implements WebMvcConfigurer { // 拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { MyInterceptor myInterceptor = new MyInterceptor(); registry.addInterceptor(myInterceptor).addPathPatterns("/**"); } }
拦截器
public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("处理器方法前调用"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("处理器方法后调用"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("渲染完成后调用"); } }
4、测试Controller
// 接受User对象修改并返回 @PostMapping("/test") @ResponseBody public User test(@RequestBody User user) { // 修改名字为李四然后返回给前台 user.setName("李四"); System.out.println(user); return user; }
启动tomcat发送请求结果
二、SpringServletContainerInitializer
1、web容器初始化入口
- 在web容器启动时为提供给第三方组件机会做一些
初始化
的工作,例如注册servlet或者filtes等- servlet规范中通过
ServletContainerInitializer接口
实现此功能
- servlet规范中通过
- 需要在对应的jar包的
META-INF/services
目录创建一个名为javax.servlet.ServletContainerInitializer
的文件- 文件内容指定具体的
ServletContainerInitializer实现类
- 文件内容指定具体的
ps:JDK会自动加载META-INF/services目录下的类(深入理解SPI机制)
容器在启动应用的时候,会扫描当前应用每一个jar包里面META-INF/services指定的实现类(tomcat默认读取)
2、SpringServletContainerInitializer的作用解析
- @HandlesTypes注解作用
- 获取到所有的实现了
WebApplicationInitializer接口
的类,然后赋值给onStartup方法的webAppInitializerClasses参数 - 官方话术为,获取当前类(SpringServletContainerInitializer)
感兴趣的类
(WebApplicationInitializer)信息
- 获取到所有的实现了
- 获取到WebApplicationInitializer实现类的Class集合,反射创建对象,遍历调用对象的
onStartup
方法
// SpringServletContainerInitializer类 @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = Collections.emptyList(); // 如果找不到WebApplicationInitializer的实现类,webAppInitializerClasses就为null if (webAppInitializerClasses != null) { initializers = new ArrayList<>(webAppInitializerClasses.size()); for (Class<?> waiClass : webAppInitializerClasses) { // 判断实现类不是接口抽象类,即正常的接口实现类 if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { // 反射创建对象,并添加到集合中,后面统一遍历调用onStartup方法 initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } // 集合为空,证明没找到实现类,直接返回 if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } // 排序,证明WebApplicationInitializer的实现类有先后顺序 AnnotationAwareOrderComparator.sort(initializers); for (WebApplicationInitializer initializer : initializers) { // 调用WebApplicationInitializer接口实现类的onStartup方法 initializer.onStartup(servletContext); } } } // WebApplicationInitializer接口 public interface WebApplicationInitializer { /** * 初始化此Web应用程序所需的任何Servlet、过滤器、侦听器上下文参数和属性配置给定的ServletContext */ void onStartup(ServletContext servletContext) throws ServletException; }
WebApplicationInitializer接口与自定义配置类WebAppInitalizer(代替web.xml)关系
- WebApplicationInitializer初始化核心接口,onStartup方法初始化Servelt、过滤器等
- WebAppInitalizer即为WebApplicationInitializer的实现类,也就是SpringServletContainerInitializer要找的感兴趣的类,获取到多个放到集合initializer中,然后排序,最后遍历调用onStartup方法
总结SpringServletContainerInitializer
作用:加载自定义的WebApplicationInitializer初始化核心接口
的实现类WebAppInitializer,调用onStartup
方法来实现web容器初始化。
三、自定义配置类的加载
自定义配置类WebAppInitializer(代替web.xml)的类图如下:
由上一节可知,web容器初始化工作会调用自定义配置类的onStartup方法,那就是根据类图从下往上找onStartup方法调用
,WebAppInitializer和AbstractAnnotationConfigDispatcherServletInitializer中都没有onStartup方法,那么首先进入AbstractDispatcherServletInitializer
重写的onStartup方法,核心内容注册前端控制器
。
1、AbstractDispatcherServletInitializer注册前端控制器
- getServletMappings():调用自定义配置类配置
DispatcherServlet的映射路径
的方法 - getServletFilters():调用自定义配置类
注册过滤器
的方法
// AbstractDispatcherServletInitializer类的方法 protected void registerDispatcherServlet(ServletContext servletContext) { // 获取servlet名称,常量“dispatcher” String servletName = getServletName(); // 创建一个web应用程序子容器 WebApplicationContext servletAppContext = createServletApplicationContext(); // 创建DispatcherServlet对象,将上下文设置到dispatcherServlet中 FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); // 设置servlet容器初始化参数(这里不设置一般默认为null) dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); // 把servlet添加到Tomcat容器中 ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); if (registration == null) { throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " + "Check if there is another servlet registered under the same name."); } // 将前端控制器初始化提前到服务器启动时,否则调用时才会初始化 registration.setLoadOnStartup(1); // 添加servlet映射,拦截请求 // 调用自定义配置类重写的getServletMappings方法 registration.addMapping(getServletMappings()); // 设置是否支持异步,默认true registration.setAsyncSupported(isAsyncSupported()); // 获取所有的过滤器getServletFilters方法 // 调用自定义配置类重写的getServletMappings方法 Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } // 空方法,可以再对dispatcherServlet进行处理 customizeRegistration(registration); }
1.1、创建web注解容器
- getServletConfigClasses():调用自定义配置类配置
SpringMVC配置
的方法
// AbstractAnnotationConfigDispatcherServletInitializer类方法 protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); // 调用自定义配置类的设置子容器配置文件的方法 Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { context.register(configClasses); } return context; }
1.2、创建DispatcherServlet对象
- 创建
DispatcherServlet
对象,传入web容器
// AbstractDispatcherServletInitializer类方法 protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { return new DispatcherServlet(servletAppContext); }
1.3、注册过滤器
- 将过滤器添加到
Tomcat
容器的过滤器集合中
protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) { String filterName = Conventions.getVariableName(filter); Dynamic registration = servletContext.addFilter(filterName, filter); ... registration.setAsyncSupported(isAsyncSupported()); registration.addMappingForServletNames(getDispatcherTypes(), false, getServletName()); return registration; }
- 对于注册过滤器是Tomcat的内容,之前文章Tomcat源码解析(五):StandardEngine、StandardHost、StandardContext、StandardWrapper中有介绍
小结一下
- 目前为止代替web.xml的配置类中内容加载完成
- 创建web注解容器,此时只是创建出来,还
没有初始化
- 创建DispatcherServlet并设置映射路径
- 注册过滤器
- 创建web注解容器,此时只是创建出来,还
- 接下来的入口在DispatcherServlet这里,因为其本质是Servlet,那么就会涉及到Tomcat初始化Servelt
2、DispatcherServlet初始化
DispatcherServlet类图如下:
Tomcat启动容器加载Servelt(这里是DispatcherServlet)并初始化,就会调用到这里。之前文章Tomcat源码解析(五):StandardEngine、StandardHost、StandardContext、StandardWrapper中有介绍。
- initWebApplicationContext方法内核心内容就是调用configureAndRefreshWebApplicationContext这个方法
- 看这方法名字中文直译:配置和刷新web容器
- 核心内容就是最后一句,
web容器的刷新
- 容器刷新
抽象类AbstractApplicationContext
的refresh
方法,看过spring源码的应该很熟悉 web容器
和spring容器
都间接继承了AbstractApplicationContext,容器刷新
都调用如下方法- 关于spring的源码Spring源码解析(三):bean容器的刷新之前介绍
容器初始化时候有个很重要的bean工厂后置处理器ConfigurationClassPostProcessor
,作用就是解析@Configuration,@Import,@ComponentScan,@Bean
等注解给bean容器添加bean定义
,之前文章Spring源码解析(六):bean工厂后置处理器ConfigurationClassPostProcessor有介绍。
接下来的入口就在自定义g配置类SpringMVCConfi
这里,因为它的配置类注解@Configuration
(也是@Component),@ComponentScan(扫描@Controller注解)
和@EnableWebMvc(导入DelegatingWebMvcConfiguration.class)
注解都会被扫描解析到。
四、@EnableWebMvc解析
@EnableWebMvc
1、WebMvcConfigurer接口
在讲DelegatingWebMvcConfiguration之前先说下WebMvcConfigurer接口,因为下面内容都是围绕着WebMvcConfigurer接口展开的。
WebMvcConfigurer是一个接口,它提供了一种扩展SpringMVC配置的方式。通过实现WebMvcConfigurer接口,可以定制化SpringMVC的配置
,例如添加拦截器、跨域设置、方法参数解析器、返回值处理、消息转换器、异常处理器
public interface WebMvcConfigurer { ... // 配置拦截器 default void addInterceptors(InterceptorRegistry registry) { } // 配置跨域 default void addCorsMappings(CorsRegistry registry) { } // 配置视图解析器 default void configureViewResolvers(ViewResolverRegistry registry) { } // 配置方法参数解析器(解析@RequestBody就是通过HandlerMethodArgumentResolver接口实现的) default void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { } // 配置方法返回值(解析@ResponseBody就是通过HandlerMethodReturnValueHandler接口实现的) default void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers) { } // 配置消息转换器(此时可能还没有转换器) default void configureMessageConverters(List<HttpMessageConverter<?>> converters) { } // 扩展消息转换器(至少已存在默认转换器) default void extendMessageConverters(List<HttpMessageConverter<?>> converters) { } // 配置异常处理器 default void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { } ... }
2、DelegatingWebMvcConfiguration
DelegatingWebMvcConfiguration的类图如下:
- 如果开发者或者第三方想要配置拦截器、消息转换器的等配置,只要实现
WebMvcConfigurer
接口重写对应方法即可 - 通过setConfigurers方法讲
所有WebMvcConfigurer接口实现类
注入进来,放入configurers的List<WebMvcConfigurer> delegates属性中 - 下面会讲到什么时候触发调用DelegatingWebMvcConfiguration中重写的MVC配置方法
@Configuration(proxyBeanMethods = false) public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { // WebMvcConfigurerComposite实现WebMvcConfigurer,内部有个WebMvcConfigurer集合 private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); // 注入一组WebMvcConfigurer,这些WebMvcConfigurer由开发人员提供,或者框架其他部分提供 @Autowired(required = false) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } } ... // 如下方法都是重写父类WebMvcConfigurationSupport // 与WebMvcConfigurer接口中的方法一样,配置拦截器、跨域配置等 @Override protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { this.configurers.configureDefaultServletHandling(configurer); } @Override protected void addInterceptors(InterceptorRegistry registry) { this.configurers.addInterceptors(registry); } @Override protected void addCorsMappings(CorsRegistry registry) { this.configurers.addCorsMappings(registry); } @Override protected void configureViewResolvers(ViewResolverRegistry registry) { this.configurers.configureViewResolvers(registry); } @Override protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { this.configurers.configureMessageConverters(converters); } @Override protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { this.configurers.configureHandlerExceptionResolvers(exceptionResolvers); } ... }
调用DelegatingWebMvcConfiguration重写的MVC配置方法实际就是对应的配置添加到对应的注册器
中。如所有的拦截器都会被添加到InterceptorRegistry(拦截器注册器)
、所有跨域配置则会被添加到CorsRegistry(跨域注册器)
,不用说对应的注册器中肯定维护着对应的配置集合。
// WebMvcConfigurerComposite类 class WebMvcConfigurerComposite implements WebMvcConfigurer { private final List<WebMvcConfigurer> delegates = new ArrayList<>(); @Override public void addInterceptors(InterceptorRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { delegate.addInterceptors(registry); } } @Override public void addCorsMappings(CorsRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { delegate.addCorsMappings(registry); } } @Override public void configureViewResolvers(ViewResolverRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configureViewResolvers(registry); } } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { for (WebMvcConfigurer delegate : this.delegates) { delegate.configureMessageConverters(converters); } } }
3、WebMvcConfigurationSupport(🔥重点)
在上面说到WebMvcConfigurationSupport类中定义了与WebMvcConfigurer接口一样的配置方法,都是空实现,由子类DelegatingWebMvcConfiguration重写,遍历所有WebMvcConfigurer的实现类,将对应配置添加到对应注册器中。
另外一方面在WebMvcConfigurationSupport类中有很多@Bean
方法,即bean定义,返回值即为创建的bean对象
。其中有两个很重要,映射器RequestMappingHandlerMapping
和适配器RequestMappingHandlerAdapter
。
3.1、处理器映射器和处理器适配器
映射器HandlerMapping
public interface HandlerMapping { /** * 获取执行器链(包括Handler和拦截器) */ @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
- HandlerMapping映射器作用:主要是根据request请求
匹配/映射
上能够处理当前request的Handler - 定义Handler的方式有很多
- 最常用的
@Controller
,结合@RequestMapping("/test")
- 实现Controller接口,实现handleRequest方法,结合@Component(“/test”)
- 实现HttpRequestHandler接口,实现handleRequest方法,结合@Component(“/test”)
- 最常用的
- 以上三种方式都需要生成请求和Handler的映射
- 所以抽象出一个接口:
HandlerMapping
,有很多实现类 - @RequestMapping注解的Handler使用
RequestMappingHandlerMapping
处理 - 其他方式会使用BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping
- 所以抽象出一个接口:
适配器HandlerAdapter
public interface HandlerAdapter { /** * 因为有多个HandlerMapping和HandlerAdapter * 对于HandlerAdapter是否支持对应的HandlerMapping,通过此方法判断 */ boolean supports(Object handler); /** * 具体调用Hangder的方法 */ @Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; }
- 上一步中不同映射器通过请求映射到的
Handler
是不确定类型
的- @RequestMapping注解方式获取到的是具体的Method
- 其他实现接口方式获取到的是具体的Class
- 此时拿到Handler去执行具体的方法时候,方式是
不统一
的 - 那么适配器就出现了
RequestMappingHandlerAdapter
就是处理@RequestMapping注解的Handler
3.2、RequestMappingHandlerMapping映射器
- 一句话解释它:解析@RequestMapping注解
3.2.1、实例化
// WebMvcConfigurationSupport类方法 @Bean @SuppressWarnings("deprecation") public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { // 创建RequestMappingHandlerMapping对象 RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); // 设置排序为0,以后会遍历所有HandlerMapping,排序0的话,则会先遍历 // 简单说@RequestMaping注解使用概率大,先用RequestMappingHandlerMapping处理 mapping.setOrder(0); // 设置所有的拦截器,并排序 mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); mapping.setContentNegotiationManager(contentNegotiationManager); // 设置所有的跨域配置 mapping.setCorsConfigurations(getCorsConfigurations()); // 省略各种匹配url的属性,如url正则匹配等等,我们这次只考虑url正常匹配 ... return mapping; }
3.2.1.1、获取所有拦截器
这里先创建拦截器注册器InterceptorRegistry
,然后调用DelegatingWebMvcConfiguration重写WebMvcConfigurationSupport的添加拦截器方法addInterceptors,这样所有拦截器就都会被添加到InterceptorRegistry registry
中。最后将我们自定义的拦截器组成MappedInterceptor
。
// WebMvcConfigurationSupport protected final Object[] getInterceptors( FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { if (this.interceptors == null) { InterceptorRegistry registry = new InterceptorRegistry(); // 这里就是调用DelegatingWebMvcConfiguration重写的方法 addInterceptors(registry); // 这是两个设置setAttribute值的拦截器,不用深入研究 registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService)); registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider)); // 这里会将上面我们自己设置的拦截器包装一层返回 this.interceptors = registry.getInterceptors(); } return this.interceptors.toArray(); }
这里将我们所有自定义拦截器都这样包装一层,因为是以后为了统一管理,这个MappedInterceptor
拦截器里面有个很重要方法matches(此次请求是否匹配当前拦截器
),就是筛选拦截器链使用的。
3.2.1.2、获取所有跨域配置
也是先创建跨域注册器CorsRegistry
,然后调用然后调用DelegatingWebMvcConfiguration重写WebMvcConfigurationSupport的添加跨域方法addCorsMappings,也是添加到CorsRegistry registry
中。
// WebMvcConfigurationSupport protected final Map<String, CorsConfiguration> getCorsConfigurations() { if (this.corsConfigurations == null) { CorsRegistry registry = new CorsRegistry(); addCorsMappings(registry); this.corsConfigurations = registry.getCorsConfigurations(); } return this.corsConfigurations; }
3.2.2、初始化
RequestMappingHandlerMapping的复杂类图看一下(有删减)
@RequestMapping注解肯定是在容器启动时候解析的,那么这个工作就放在RequestMappingHandlerMapping这个bean对象的初始化阶段来完成。之前文章Spring源码解析(四):单例bean的创建流程有介绍过,bean对象创建后会调用各种初始化方法,其实就包括调用InitializingBean接口
的afterPropertiesSet
方法来实现初始化
。
- RequestMappingHandlerMapping实现了afterPropertiesSet方法如下
// RequestMappingHandlerMapping类方法 @Override @SuppressWarnings("deprecation") public void afterPropertiesSet() { // 创建RequestMappingInfo对象 this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); if (getPatternParser() != null) { this.config.setPatternParser(getPatternParser()); Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch, "Suffix pattern matching not supported with PathPatternParser."); } else { this.config.setSuffixPatternMatch(useSuffixPatternMatch()); this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch()); this.config.setPathMatcher(getPathMatcher()); } // 调用父类实现的afterPropertiesSet方法 super.afterPropertiesSet(); }
- 父类AbstractHandlerMethodMapping实现了afterPropertiesSet方法如下
// AbstractHandlerMethodMapping类方法 /** * 在初始化时检测处理程序方法 */ @Override public void afterPropertiesSet() { initHandlerMethods(); } /** * 扫描 ApplicationContext 中的 Bean,检测并注册处理程序方法 */ protected void initHandlerMethods() { // 获取所有的bean对象并遍历 for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { // 筛选候选的bean processCandidateBean(beanName); } } // getHandlerMethods()获取请求路径与具体Controller方法的映射关系 handlerMethodsInitialized(getHandlerMethods()); }
3.2.2.1、获取候选bean
// AbstractHandlerMethodMapping类方法 protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { // 获取bean对象的Class类型 beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { ... } // 判断是否处理程序 if (beanType != null && isHandler(beanType)) { // 查找处理程序的方法 detectHandlerMethods(beanName); } }
- 根据类上
@Controller
或@RequestMapping
注解判断是否为处理程序
// RequestMappingHandlerMapping @Override protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
- 查询处理程序的方法
- 此时获取到的handler已经确定有@Controller或者@RequestMapping
- 遍历handler类下所有的Method
- 判断
Method
方法上是否有@RequestMapping
注解 - 有注解则将注解内的属性包装成一个类:
RequestMappingInfo
- 判断
- 返回一个map集合methods
- key为有@RequestMapping注解的
Method
对象 - value为
RequestMappingInfo
对象
- key为有@RequestMapping注解的
// AbstractHandlerMethodMapping类方法 protected void detectHandlerMethods(Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { // 类如果被代理,获取真正的类型 Class<?> userType = ClassUtils.getUserClass(handlerType); Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { return getMappingForMethod(method, userType); } catch (Throwable ex) { ... } }); ... // 注册Handler方法(将请求路径、RequestMappingInfo、Controller#Method统一放一起) methods.forEach((method, mapping) -> { Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); registerHandlerMethod(handler, invocableMethod, mapping); }); } } // RequestMappingHandlerMapping类方法 @Override @Nullable protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); ... return info; }
- getMappingForMethod方法的实现就交给具体子类RequestMappingHandlerMapping了
- getMappingForMethod方法核心内容:筛选
Controller类
下@RequestMapping
注解方法
registerHandlerMethod:注册Handler方法(将请求路径、RequestMappingInfo、Controller#Method统一放到MappingRegistry对象中)
- 核心方法为AbstractHandlerMethodMapping抽象类的
内部类MappingRegistry
类的register
方法
// AbstractHandlerMethodMapping的内部类MappingRegistry类方法 class MappingRegistry { private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>(); private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { // handler(Controller对象)与有@RequestMapping的Method组成对象HandlerMethod HandlerMethod handlerMethod = createHandlerMethod(handler, method); // 校验@RequestMapping注解内容是否注册过 validateMethodMapping(handlerMethod, mapping); // 获取@RequestMapping注解映射的路径,因为可以设置多个,所以这里是集合 Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { this.pathLookup.add(path, mapping); } ... // 获取@CrossOrigin获取CorsConfiguration,并初始化跨域的默认值,最后跨域配置添加到corsLookup CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { corsConfig.validateAllowCredentials(); this.corsLookup.put(handlerMethod, corsConfig); } // 各类属性配置组成对象MappingRegistration,然后添加到registry中 this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null)); } finally { this.readWriteLock.writeLock().unlock(); } } }
MappingRegistry
(映射注册器),以后的mapping映射什么都放这里- MultiValueMap<String, T> pathLookup
- key为
@RequestMapping
注解的映射路径
- value为
RequestMappingInfo
对象(@RequestMapping注解属性组成的对象)
- key为
- Map<HandlerMethod, CorsConfiguration> corsLookup
- key为HandlerMethod
- value为解析
@CrossOrigin
注解的跨域配置
- Map<T, MappingRegistration> registry
- key为
RequestMappingInfo
对象 - value为
MappingRegistration
对象(RequestMappingInfo、HandlerMethod、CorsConfiguration组成的对象)
- key为
- MultiValueMap<String, T> pathLookup
- 校验RequestMappingInfo(@RequestMapping属性对象)是否存在
- 存在的话这里就会抛出异常
There is already 'xxx' bean method
(这个大家应该很熟悉)
- 存在的话这里就会抛出异常
- 注意:以上步骤都是在readWriteLock锁内完成的,以防多个线程注册映射对象重复
3.2.2.2、获取HandlerMethod并统计数量
- 从mappingRegistry中获取map集合即为上面所说MappingRegistry对象的registry属性
// AbstractHandlerMethodMapping类方法 public Map<T, HandlerMethod> getHandlerMethods() { this.mappingRegistry.acquireReadLock(); try { return Collections.unmodifiableMap( this.mappingRegistry.getRegistrations().entrySet() .stream().collect(Collectors.toMap( Map.Entry::getKey, entry -> entry.getValue().handlerMethod) )); } finally { this.mappingRegistry.releaseReadLock(); } }
- 获取到上面拿到的handlerMethods,打印数量
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) { // 获取到上面拿到的值,打印数量 int total = handlerMethods.size(); if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) { logger.debug(total + " mappings in " + formatMappingName()); } }
3.3、RequestMappingHandlerAdapter适配器
- 一句话解释它:拿到RequestMappingHandlerMapping解析出的HandlerMethod去调用具体的Method
3.3.1、实例化
// WebMvcConfigurationSupport类方法 private static final boolean jackson2Present; // 加载对应的类,能加载成功方true,不能加载成功,表示没有这个类,没有导入包,返回false jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcValidator") Validator validator) { // 创建RequestMappingHandlerAdapter对象 RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); adapter.setContentNegotiationManager(contentNegotiationManager); // 设置消息转换器 adapter.setMessageConverters(getMessageConverters()); adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator)); adapter.setCustomArgumentResolvers(getArgumentResolvers()); adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); // 如果导入Jackson包,则添加 if (jackson2Present) { // 处理jackson中的@JsonView的字段,过滤输出的字段,具体实现以后单独讲 adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice())); adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice())); } // 省略异步配置,以后再研究 ... return adapter; }
3.3.1.1、获取消息转换器
先调用DelegatingWebMvcConfiguration重写的方法,也就是遍历所有WebMvcConfigurer实现类
,调用他们的configureMessageConverters
方法,新增
的消息转换器都会添加到messageConverters集合中
。如果开发者和第三方都没有添加,那么设置默认的消息转换器,设置完以后,再调用扩展方法
,也就是遍历所有WebMvcConfigurer实现类,调用他们的extendMessageConverters
方法,对消息转换器做最后修改
。
// WebMvcConfigurationSupport protected final List<HttpMessageConverter<?>> getMessageConverters() { if (this.messageConverters == null) { this.messageConverters = new ArrayList<>(); // 这里就是调用DelegatingWebMvcConfiguration重写的方法,配置消息转换器 configureMessageConverters(this.messageConverters); if (this.messageConverters.isEmpty()) { // 如果上面没有添加消息转换器,这里添加默认的消息转换器 addDefaultHttpMessageConverters(this.messageConverters); } // 这里就是调用DelegatingWebMvcConfiguration重写的方法,扩展消息转化器 extendMessageConverters(this.messageConverters); } return this.messageConverters; }
加载jackson里的类,能加载成功jackson2Present返回true,不能加载成功,表示没有这个类,没有导入包,返回jackson2Present返回false,然后去判断是否导入Google、JDK自带的处理JSON类。一般我们会导入Jackson
,那么这里会添加MappingJackson2HttpMessageConverter
消息转换器。
// WebMvcConfigurationSupport类方法 protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) { // 默认消息转换器 messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new ResourceRegionHttpMessageConverter()); ... // jackson if (jackson2Present) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build())); } // Google Gson 库中的一个核心类,Java对象与JSON 格式字符串进行相互转换 else if (gsonPresent) { messageConverters.add(new GsonHttpMessageConverter()); } // JDK 类库JSON类 else if (jsonbPresent) { messageConverters.add(new JsonbHttpMessageConverter()); } ... }
3.3.2、初始化
RequestMappingHandlerAdapter的类图如下(有删减)
与RequestMappingHandlerMapping一样,RequestMappingHandlerAdapter对象初始化就会调用InitializingBean接口
的afterPropertiesSet
方法来实现初始化
。
- RequestMappingHandlerAdapter实现了afterPropertiesSet方法如下
// RequestMappingHandlerAdapter类方法 @Override public void afterPropertiesSet() { // 初始化@ControllerAdvice 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); } }
3.3.2.1、初始化@ControllerAdvice
- 遍历所有的bean对象,获取有
@ControllerAdvice
注解的bean - 通过@ControllerAdvice内容和bean名称创建对象ControllerAdviceBean,添加到集合中
- 再从集合中筛选出bean对象实现接口
RequestBodyAdvice
或ResponseBodyAdvice
- RequestBodyAdvice请求增强器:允许请求体被读取并转换为对象
- ResponseBodyAdvice响应增强器:允许在执行@ResponseBody后自定义返回数据
// RequestMappingHandlerAdapter类方法 private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } // 获取所有@ControllerAdvice注解的bean,创建成ControllerAdviceBean对象的集合 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); List<Object> requestResponseBodyAdviceBeans = new ArrayList<>(); for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } ... // 筛选实现RequestBodyAdvice或ResponseBodyAdvice的adviceBean if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } } if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); } ... } public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) { ListableBeanFactory beanFactory = context; if (context instanceof ConfigurableApplicationContext) { beanFactory = ((ConfigurableApplicationContext) context).getBeanFactory(); } List<ControllerAdviceBean> adviceBeans = new ArrayList<>(); // 遍历所有的bean对象 for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Object.class)) { if (!ScopedProxyUtils.isScopedTarget(name)) { // 寻找有@ControllerAdvice注解 ControllerAdvice controllerAdvice = beanFactory.findAnnotationOnBean(name, ControllerAdvice.class); if (controllerAdvice != null) { // 创建ControllerAdviceBean对象添加到集合中 adviceBeans.add(new ControllerAdviceBean(name, beanFactory, controllerAdvice)); } } } OrderComparator.sort(adviceBeans); return adviceBeans; }
3.3.2.2、HandlerMethodArgumentResolver方法参数解析器
public interface HandlerMethodArgumentResolver { /** * 此解析器是否支持给定的方法参数 */ boolean supportsParameter(MethodParameter parameter); /** * 将方法参数解析为给定请求的参数值 */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }
- 方法参数解析器有很多,差不多一个注解就对应一个参数解析器
3.3.2.3、HandlerMethodReturnValueHandler返回值处理器
public interface HandlerMethodReturnValueHandler { /** * 此处理程序是否支持给定的方法返回类型 */ boolean supportsReturnType(MethodParameter returnType); /** * 通过向模型添加属性并设置视图或设置 */ void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
- 返回值处理器也有很多,我们只研究熟悉常用的就可以
RequestResponseBodyMethodProcessor
类既是@RequestBody
解析器又是@ResponseBody
解析器
对应方法参数解析器和返回值处理器这里就不展开说了,只要知道项目启动会加载这些东西即可,下一篇文章请求的执行流程再进入源码研究。
3.4、HandlerExceptionResolver异常处理器
- 一句话解释它:Hander异常抛错后调用的方法
与消息转换器很像,先遍历所有WebMvcConfigurer实现类configureHandlerExceptionResolvers中新增
的异常处理器,如果没有开发者或者第三方新增,那么添加默认的的异常处理器,再调用扩展方法,也就是遍历所有WebMvcConfigurer实现类,调用他们的extendHandlerExceptionResolvers方法,对异常处理器做最后修改
。
// WebMvcConfigurationSupport类方法 @Bean public HandlerExceptionResolver handlerExceptionResolver( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) { // 创建空集合 List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>(); // 这里就是调用DelegatingWebMvcConfiguration重写的方法 // 其实就是寻找WebMvcConfigurer实现类是否添加异常处理器 configureHandlerExceptionResolvers(exceptionResolvers); if (exceptionResolvers.isEmpty()) { addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager); } // 这里就是调用DelegatingWebMvcConfiguration重写的方法,扩展异常处理器 extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); composite.setExceptionResolvers(exceptionResolvers); return composite; }
- ExceptionHandlerExceptionResolver:这个异常处理器内
包含
所有的自定义异常处理器- 因为这个对象不是bean对象,这里手动调用初始化接口的方法afterPropertiesSet
- 最后添加默认异常处理器DefaultHandlerExceptionResolver
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers, ContentNegotiationManager mvcContentNegotiationManager) { // 这个异常处理器内包含所有的自定义异常处理器 ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver(); exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager); exceptionHandlerResolver.setMessageConverters(getMessageConverters()); exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers()); exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { exceptionHandlerResolver.setResponseBodyAdvice( Collections.singletonList(new JsonViewResponseBodyAdvice())); } if (this.applicationContext != null) { exceptionHandlerResolver.setApplicationContext(this.applicationContext); } // 手动调用初始化接口的初始化方法 exceptionHandlerResolver.afterPropertiesSet(); exceptionResolvers.add(exceptionHandlerResolver); // ResponseStatus异常处理器 ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver(); responseStatusResolver.setMessageSource(this.applicationContext); exceptionResolvers.add(responseStatusResolver); // 默认异常处理器 exceptionResolvers.add(new DefaultHandlerExceptionResolver()); }
3.4.1、加载自定义异常处理器
// ExceptionHandlerExceptionResolver类方法 @Override public void afterPropertiesSet() { // 初始化异常处理增强类 initExceptionHandlerAdviceCache(); if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); } }
- 筛选异常处理器类:
@ControllerAdvice
类且方法上存在@ExceptionHandler
// ExceptionHandlerExceptionResolver类方法 private void initExceptionHandlerAdviceCache() { if (getApplicationContext() == null) { return; } // 获取所有@ControllerAdvice注解的bean,创建成ControllerAdviceBean,添加到集合中 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } // 筛选方法上@ExceptionHandler注解 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); if (resolver.hasExceptionMappings()) { this.exceptionHandlerAdviceCache.put(adviceBean, resolver); } if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) { this.responseBodyAdvice.add(adviceBean); } } } // ExceptionHandlerMethodResolver类属性和方法 public static final MethodFilter EXCEPTION_HANDLER_METHODS = method -> AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class); public ExceptionHandlerMethodResolver(Class<?> handlerType) { for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) { for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) { addExceptionMapping(exceptionType, method); } } }
至此,SpringMVC启动重要的组件都介绍完毕。
总结
- 加载继承
AbstractAnnotationConfigDispatcherServletInitializer
的MVC配置类(开发者创建,代替web.xml
) - 既然代替web.xml那么这个配置类可以设置
DispatcherServlet的映射路径
,注册过滤器
- 父类AbstractAnnotationConfigDispatcherServletInitializer里面会
创建web注解容器
、创建DispatcherServlet对象
、添加过滤器
到Tomcat容器的过滤器集合中- DispatcherServlet初始化触发了web容器的刷新,加载所有
@Controller
注解的bean
- DispatcherServlet初始化触发了web容器的刷新,加载所有
- 如果开发者或者第三方想要
配置拦截器
、消息转换器
的等配置,只要实现WebMvcConfigurer
接口重写对应方法即可 - 解析
@RequestMapping
注解(根据注解属性创建对象RequestMappingInfo)- 遍历所有的bean,获取
类上
是否有@Controller
或@RequestMapping
注解的bean - 再遍历所有的方法,筛选
方法上
是否有@RequestMapping
注解 - 最后注册成
两个map
存起来,以后映射方法从这里获取- MappingRegistry#MultiValueMap<String, T>
pathLookup
- key为
@RequestMapping
注解的映射路径
- value为
RequestMappingInfo
对象
- key为
- MappingRegistry#Map<T, MappingRegistration>
registry
- key为
RequestMappingInfo
对象 - value为
MappingRegistration
对象(包含Controller#Method)
- key为
- MappingRegistry#MultiValueMap<String, T>
- 遍历所有的bean,获取
- 解析
@ControllerAdvice
注解- 遍历所有的bean,筛选
类上
有@ControllerAdvice
注解的bean- 如果bean实现接口
RequestBodyAdvice
或ResponseBodyAdvice
,那就是请求响应数据增强器 - 如果bean有方法存在
@ExceptionHandler
,那就是异常处理器
- 如果bean实现接口
- 遍历所有的bean,筛选