0


SpringMVC源码解析(一):web容器启动流程

SpringMVC源码系列文章

SpringMVC源码解析(一):web容器启动流程


目录

一、SpringMVC全注解配置

1、pom文件

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="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中配置的
 */publicclassWebAppInitializerextendsAbstractAnnotationConfigDispatcherServletInitializer{/**
     * Spring的配置,目前不涉及Spring,这里设置为空
     */@OverrideprotectedClass<?>[]getRootConfigClasses(){returnnewClass<?>[0];}/**
     * SpringMVC的配置
     */@OverrideprotectedClass<?>[]getServletConfigClasses(){returnnewClass<?>[]{SpringMVCConfig.class};}/**
     * 用于配置DispatcherServlet的映射路径
     */@OverrideprotectedString[]getServletMappings(){returnnewString[]{"/"};}/**
     * 注册过滤器
     */@OverrideprotectedFilter[]getServletFilters(){// 字符编码过滤器CharacterEncodingFilter characterEncodingFilter =newCharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceEncoding(true);// HiddenHttpMethodFilter 用于支持 PUT、DELETE 等 HTTP 请求HiddenHttpMethodFilter hiddenHttpMethodFilter =newHiddenHttpMethodFilter();returnnewFilter[]{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注解驱动@EnableWebMvcpublicclassSpringMVCConfigimplementsWebMvcConfigurer{// 拦截器@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){MyInterceptor myInterceptor =newMyInterceptor();
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");}}

拦截器

publicclassMyInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{System.out.println("处理器方法前调用");returntrue;}@OverridepublicvoidpostHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView)throwsException{System.out.println("处理器方法后调用");}@OverridepublicvoidafterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex)throwsException{System.out.println("渲染完成后调用");}}

4、测试Controller

// 接受User对象修改并返回@PostMapping("/test")@ResponseBodypublicUsertest(@RequestBodyUser user){// 修改名字为李四然后返回给前台
    user.setName("李四");System.out.println(user);return user;}

启动tomcat发送请求结果

在这里插入图片描述

二、SpringServletContainerInitializer

1、web容器初始化入口

  • 在web容器启动时为提供给第三方组件机会做一些初始化的工作,例如注册servlet或者filtes等- servlet规范中通过ServletContainerInitializer接口实现此功能
  • 需要在对应的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)publicclassSpringServletContainerInitializerimplementsServletContainerInitializer{@OverridepublicvoidonStartup(@NullableSet<Class<?>> webAppInitializerClasses,ServletContext servletContext)throwsServletException{List<WebApplicationInitializer> initializers =Collections.emptyList();// 如果找不到WebApplicationInitializer的实现类,webAppInitializerClasses就为nullif(webAppInitializerClasses !=null){
            initializers =newArrayList<>(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){thrownewServletException("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接口publicinterfaceWebApplicationInitializer{/**
     * 初始化此Web应用程序所需的任何Servlet、过滤器、侦听器上下文参数和属性配置给定的ServletContext
     */voidonStartup(ServletContext servletContext)throwsServletException;}

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类的方法protectedvoidregisterDispatcherServlet(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){thrownewIllegalStateException("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类方法protectedWebApplicationContextcreateServletApplicationContext(){AnnotationConfigWebApplicationContext context =newAnnotationConfigWebApplicationContext();// 调用自定义配置类的设置子容器配置文件的方法Class<?>[] configClasses =getServletConfigClasses();if(!ObjectUtils.isEmpty(configClasses)){
        context.register(configClasses);}return context;}

1.2、创建DispatcherServlet对象

  • 创建DispatcherServlet对象,传入web容器
// AbstractDispatcherServletInitializer类方法protectedFrameworkServletcreateDispatcherServlet(WebApplicationContext servletAppContext){returnnewDispatcherServlet(servletAppContext);}

1.3、注册过滤器

  • 将过滤器添加到Tomcat容器的过滤器集合中
protectedFilterRegistration.DynamicregisterServletFilter(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并设置映射路径- 注册过滤器
  • 接下来的入口在DispatcherServlet这里,因为其本质是Servlet,那么就会涉及到Tomcat初始化Servelt

2、DispatcherServlet初始化

DispatcherServlet类图如下:

在这里插入图片描述

Tomcat启动容器加载Servelt(这里是DispatcherServlet)并初始化,就会调用到这里。之前文章Tomcat源码解析(五):StandardEngine、StandardHost、StandardContext、StandardWrapper中有介绍。

在这里插入图片描述

  • initWebApplicationContext方法内核心内容就是调用configureAndRefreshWebApplicationContext这个方法
  • 看这方法名字中文直译:配置和刷新web容器
  • 核心内容就是最后一句,web容器的刷新

在这里插入图片描述

  • 容器刷新抽象类AbstractApplicationContextrefresh方法,看过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的配置

,例如添加拦截器、跨域设置、方法参数解析器、返回值处理、消息转换器、异常处理器**

publicinterfaceWebMvcConfigurer{...// 配置拦截器defaultvoidaddInterceptors(InterceptorRegistry registry){}// 配置跨域defaultvoidaddCorsMappings(CorsRegistry registry){}// 配置视图解析器defaultvoidconfigureViewResolvers(ViewResolverRegistry registry){}// 配置方法参数解析器(解析@RequestBody就是通过HandlerMethodArgumentResolver接口实现的)defaultvoidaddArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers){}// 配置方法返回值(解析@ResponseBody就是通过HandlerMethodReturnValueHandler接口实现的)defaultvoidaddReturnValueHandlers(List<HandlerMethodReturnValueHandler> handlers){}// 配置消息转换器(此时可能还没有转换器)defaultvoidconfigureMessageConverters(List<HttpMessageConverter<?>> converters){}// 扩展消息转换器(至少已存在默认转换器)defaultvoidextendMessageConverters(List<HttpMessageConverter<?>> converters){}// 配置异常处理器defaultvoidconfigureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers){}...}

2、DelegatingWebMvcConfiguration

DelegatingWebMvcConfiguration的类图如下:
在这里插入图片描述

  • 如果开发者或者第三方想要配置拦截器、消息转换器的等配置,只要实现WebMvcConfigurer接口重写对应方法即可
  • 通过setConfigurers方法讲所有WebMvcConfigurer接口实现类注入进来,放入configurers的List<WebMvcConfigurer> delegates属性中
  • 下面会讲到什么时候触发调用DelegatingWebMvcConfiguration中重写的MVC配置方法
@Configuration(proxyBeanMethods =false)publicclassDelegatingWebMvcConfigurationextendsWebMvcConfigurationSupport{// WebMvcConfigurerComposite实现WebMvcConfigurer,内部有个WebMvcConfigurer集合privatefinalWebMvcConfigurerComposite configurers =newWebMvcConfigurerComposite();// 注入一组WebMvcConfigurer,这些WebMvcConfigurer由开发人员提供,或者框架其他部分提供@Autowired(required =false)publicvoidsetConfigurers(List<WebMvcConfigurer> configurers){if(!CollectionUtils.isEmpty(configurers)){this.configurers.addWebMvcConfigurers(configurers);}}...// 如下方法都是重写父类WebMvcConfigurationSupport// 与WebMvcConfigurer接口中的方法一样,配置拦截器、跨域配置等@OverrideprotectedvoidconfigureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){this.configurers.configureDefaultServletHandling(configurer);}@OverrideprotectedvoidaddInterceptors(InterceptorRegistry registry){this.configurers.addInterceptors(registry);}@OverrideprotectedvoidaddCorsMappings(CorsRegistry registry){this.configurers.addCorsMappings(registry);}@OverrideprotectedvoidconfigureViewResolvers(ViewResolverRegistry registry){this.configurers.configureViewResolvers(registry);}@OverrideprotectedvoidconfigureMessageConverters(List<HttpMessageConverter<?>> converters){this.configurers.configureMessageConverters(converters);}@OverrideprotectedvoidconfigureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers){this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);}...}

**调用DelegatingWebMvcConfiguration重写的MVC配置方法实际就是

对应的配置添加到对应的注册器

中。如所有的拦截器都会被添加到

InterceptorRegistry(拦截器注册器)

、所有跨域配置则会被添加到

CorsRegistry(跨域注册器)

,不用说对应的注册器中肯定维护着对应的配置集合。**

// WebMvcConfigurerComposite类classWebMvcConfigurerCompositeimplementsWebMvcConfigurer{privatefinalList<WebMvcConfigurer> delegates =newArrayList<>();@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){for(WebMvcConfigurer delegate :this.delegates){
            delegate.addInterceptors(registry);}}@OverridepublicvoidaddCorsMappings(CorsRegistry registry){for(WebMvcConfigurer delegate :this.delegates){
            delegate.addCorsMappings(registry);}}@OverridepublicvoidconfigureViewResolvers(ViewResolverRegistry registry){for(WebMvcConfigurer delegate :this.delegates){
            delegate.configureViewResolvers(registry);}}@OverridepublicvoidconfigureMessageConverters(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

  • HandlerMapping映射器作用:主要是根据request请求匹配/映射上能够处理当前request的Handler
  • 定义Handler的方式有很多- 最常用的@Controller,结合@RequestMapping("/test")- 实现Controller接口,实现handleRequest方法,结合@Component(“/test”)- 实现HttpRequestHandler接口,实现handleRequest方法,结合@Component(“/test”)
  • 以上三种方式都需要生成请求和Handler的映射- 所以抽象出一个接口:HandlerMapping,有很多实现类- @RequestMapping注解的Handler使用RequestMappingHandlerMapping处理- 其他方式会使用BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping

适配器HandlerAdapter

  • 上一步中不同映射器通过请求映射到的Handler不确定类型- @RequestMapping注解方式获取到的是具体的Method- 其他实现接口方式获取到的是具体的Class
  • 此时拿到Handler去执行具体的方法时候,方式是不统一
  • 那么适配器就出现了
  • RequestMappingHandlerAdapter就是处理@RequestMapping注解的Handler

3.2、RequestMappingHandlerMapping映射器

  • 一句话解释它:解析@RequestMapping注解
3.2.1、实例化
// WebMvcConfigurationSupport类方法@Bean@SuppressWarnings("deprecation")publicRequestMappingHandlerMappingrequestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager")ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService")FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider")ResourceUrlProvider resourceUrlProvider){// 创建RequestMappingHandlerMapping对象RequestMappingHandlerMapping mapping =createRequestMappingHandlerMapping();
    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

中。**

// WebMvcConfigurationSupportprotectedfinalObject[]getInterceptors(FormattingConversionService mvcConversionService,ResourceUrlProvider mvcResourceUrlProvider){if(this.interceptors ==null){InterceptorRegistry registry =newInterceptorRegistry();// 这里就是调用DelegatingWebMvcConfiguration重写的方法addInterceptors(registry);
        registry.addInterceptor(newConversionServiceExposingInterceptor(mvcConversionService));
        registry.addInterceptor(newResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));this.interceptors = registry.getInterceptors();}returnthis.interceptors.toArray();}
3.2.1.2、获取所有跨域配置

**也是先

创建跨域注册器CorsRegistry

,然后调用然后调用DelegatingWebMvcConfiguration重写WebMvcConfigurationSupport的添加跨域方法addCorsMappings,也是添加到

CorsRegistry registry

中。**

// WebMvcConfigurationSupportprotectedfinalMap<String,CorsConfiguration>getCorsConfigurations(){if(this.corsConfigurations ==null){CorsRegistry registry =newCorsRegistry();addCorsMappings(registry);this.corsConfigurations = registry.getCorsConfigurations();}returnthis.corsConfigurations;}
3.2.2、初始化

RequestMappingHandlerMapping的复杂类图看一下(有删减)

**@RequestMapping注解肯定是在容器启动时候解析的,那么这个工作就放在RequestMappingHandlerMapping这个bean对象的初始化阶段来完成。之前文章Spring源码解析(四):单例bean的创建流程有介绍过,bean对象创建后会调用各种初始化方法,其实就包括调用

InitializingBean接口

afterPropertiesSet

方法来

实现初始化

。**

在这里插入图片描述

  • RequestMappingHandlerMapping实现了afterPropertiesSet方法如下
// RequestMappingHandlerMapping类方法@Override@SuppressWarnings("deprecation")publicvoidafterPropertiesSet(){// 创建RequestMappingInfo对象this.config =newRequestMappingInfo.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类方法/**
 * 在初始化时检测处理程序方法
 */@OverridepublicvoidafterPropertiesSet(){initHandlerMethods();}/**
 * 扫描 ApplicationContext 中的 Bean,检测并注册处理程序方法
 */protectedvoidinitHandlerMethods(){// 获取所有的bean对象并遍历for(String beanName :getCandidateBeanNames()){if(!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)){// 筛选候选的beanprocessCandidateBean(beanName);}}// getHandlerMethods()获取请求路径与具体Controller方法的映射关系handlerMethodsInitialized(getHandlerMethods());}
3.2.2.1、获取候选bean
// AbstractHandlerMethodMapping类方法protectedvoidprocessCandidateBean(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@OverrideprotectedbooleanisHandler(Class<?> beanType){return(AnnotatedElementUtils.hasAnnotation(beanType,Controller.class)||AnnotatedElementUtils.hasAnnotation(beanType,RequestMapping.class));}
  • 查询处理程序的方法1. 此时获取到的handler已经确定有@Controller或者@RequestMapping2. 遍历handler类下所有的Method- 判断Method方法上是否有@RequestMapping注解- **有注解则将注解内的属性包装成一个类:RequestMappingInfo**3. 返回一个map集合methods- key为有@RequestMapping注解的Method对象- value为RequestMappingInfo对象
// AbstractHandlerMethodMapping类方法protectedvoiddetectHandlerMethods(Object handler){Class<?> handlerType =(handler instanceofString?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{returngetMappingForMethod(method, userType);}catch(Throwable ex){...}});...// 这里有做了一些列的包装处理
        methods.forEach((method, mapping)->{Method invocableMethod =AopUtils.selectInvocableMethod(method, userType);registerHandlerMethod(handler, invocableMethod, mapping);});}}

我简述下获取到methods以后的的包装处理,就是各种包装对象,就不放代码了,没啥意思

  • 首先创建一个MappingRegistry(映射注册器),以后的mapping映射什么都放这里
  • handler(也就是Controller类对象)和有@RequestMapping的Method组成对象HandlerMethod
  • 获取到HandlerMethod后,这时候会校验RequestMappingInfo(@RequestMapping属性对象)是否存在- 存在的话这里就会抛出异常There is already 'xxx' bean method(这个大家应该很熟悉)
  • 最后一步往MappingRegistryMap<T, MappingRegistration<T>> registry属性中注册内容- key为RequestMappingInfo对象(@RequestMapping注解属性,包括映射路径)- value为由RequestMappingInfo和HandlerMethod组成的新对象MappingRegistration
  • 注意:以上步骤都是在readWriteLock锁内完成的,以防多个线程注册映射对象重复
3.2.2.2、获取HandlerMethod并统计数量
  • 从mappingRegistry中获取map集合,key为RequestMappingInfo,value为HandlerMethod
// AbstractHandlerMethodMapping类方法publicMap<T,HandlerMethod>getHandlerMethods(){this.mappingRegistry.acquireReadLock();try{returnCollections.unmodifiableMap(this.mappingRegistry.getRegistrations().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, 
                    entry -> entry.getValue().handlerMethod)));}finally{this.mappingRegistry.releaseReadLock();}}
  • 获取到上面拿到的handlerMethods,打印数量
protectedvoidhandlerMethodsInitialized(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类方法privatestaticfinalboolean jackson2Present;// 加载对应的类,能加载成功方true,不能加载成功,表示没有这个类,没有导入包,返回false
jackson2Present =ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)&&ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);@BeanpublicRequestMappingHandlerAdapterrequestMappingHandlerAdapter(@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(newJsonViewRequestBodyAdvice()));
        adapter.setResponseBodyAdvice(Collections.singletonList(newJsonViewResponseBodyAdvice()));}// 省略异步配置,以后再研究...return adapter;}
3.3.1.1、获取消息转换器

**先调用DelegatingWebMvcConfiguration重写的方法,也就是遍历所有

WebMvcConfigurer实现类

,调用他们的

configureMessageConverters

方法,

新增

的消息转换器都会添加到

messageConverters集合中

。如果开发者和第三方都没有添加,那么设置默认的消息转换器,设置完以后,再调用

扩展方法

,也就是遍历所有WebMvcConfigurer实现类,调用他们的

extendMessageConverters

方法,对消息转换器做最后

修改

。**

// WebMvcConfigurationSupportprotectedfinalList<HttpMessageConverter<?>>getMessageConverters(){if(this.messageConverters ==null){this.messageConverters =newArrayList<>();// 这里就是调用DelegatingWebMvcConfiguration重写的方法,配置消息转换器configureMessageConverters(this.messageConverters);if(this.messageConverters.isEmpty()){// 如果上面没有添加消息转换器,这里添加默认的消息转换器addDefaultHttpMessageConverters(this.messageConverters);}// 这里就是调用DelegatingWebMvcConfiguration重写的方法,扩展消息转化器extendMessageConverters(this.messageConverters);}returnthis.messageConverters;}

**加载jackson里的类,能加载成功jackson2Present返回true,不能加载成功,表示没有这个类,没有导入包,返回jackson2Present返回false,然后去判断是否导入Google、JDK自带的处理JSON类。一般我们会导入

Jackson

,那么这里会添加

MappingJackson2HttpMessageConverter

消息转换器。**

// WebMvcConfigurationSupport类方法protectedfinalvoidaddDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters){// 默认消息转换器
    messageConverters.add(newByteArrayHttpMessageConverter());
    messageConverters.add(newStringHttpMessageConverter());
    messageConverters.add(newResourceHttpMessageConverter());
    messageConverters.add(newResourceRegionHttpMessageConverter());...// jacksonif(jackson2Present){Jackson2ObjectMapperBuilder builder =Jackson2ObjectMapperBuilder.json();if(this.applicationContext !=null){
            builder.applicationContext(this.applicationContext);}
        messageConverters.add(newMappingJackson2HttpMessageConverter(builder.build()));}// Google Gson 库中的一个核心类,Java对象与JSON 格式字符串进行相互转换elseif(gsonPresent){
        messageConverters.add(newGsonHttpMessageConverter());}// JDK 类库JSON类elseif(jsonbPresent){
        messageConverters.add(newJsonbHttpMessageConverter());}...}
3.3.2、初始化

RequestMappingHandlerAdapter的类图如下(有删减)

**与RequestMappingHandlerMapping一样,RequestMappingHandlerAdapter对象初始化就会调用

InitializingBean接口

afterPropertiesSet

方法来

实现初始化

。**

在这里插入图片描述

  • RequestMappingHandlerAdapter实现了afterPropertiesSet方法如下
// RequestMappingHandlerAdapter类方法@OverridepublicvoidafterPropertiesSet(){// 初始化@ControllerAdviceinitControllerAdviceCache();// 设置默认的方法参数解析器if(this.argumentResolvers ==null){List<HandlerMethodArgumentResolver> resolvers =getDefaultArgumentResolvers();this.argumentResolvers =newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if(this.initBinderArgumentResolvers ==null){List<HandlerMethodArgumentResolver> resolvers =getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers =newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);}// 设置默认的返回值处理器if(this.returnValueHandlers ==null){List<HandlerMethodReturnValueHandler> handlers =getDefaultReturnValueHandlers();this.returnValueHandlers =newHandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
3.3.2.1、初始化@ControllerAdvice
  • 遍历所有的bean对象,获取有@ControllerAdvice注解的bean
  • 通过@ControllerAdvice内容和bean名称创建对象ControllerAdviceBean,添加到集合中
  • 再从集合中筛选出bean对象实现接口RequestBodyAdviceResponseBodyAdvice- RequestBodyAdvice请求增强器:允许请求体被读取并转换为对象- ResponseBodyAdvice响应增强器:允许在执行@ResponseBody后自定义返回数据
// RequestMappingHandlerAdapter类方法privatevoidinitControllerAdviceCache(){if(getApplicationContext()==null){return;}// 获取所有@ControllerAdvice注解的bean,创建成ControllerAdviceBean对象的集合List<ControllerAdviceBean> adviceBeans =ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());List<Object> requestResponseBodyAdviceBeans =newArrayList<>();for(ControllerAdviceBean adviceBean : adviceBeans){Class<?> beanType = adviceBean.getBeanType();if(beanType ==null){thrownewIllegalStateException("Unresolvable type for ControllerAdviceBean: "+ adviceBean);}...// 筛选实现RequestBodyAdvice或ResponseBodyAdvice的adviceBeanif(RequestBodyAdvice.class.isAssignableFrom(beanType)||ResponseBodyAdvice.class.isAssignableFrom(beanType)){
            requestResponseBodyAdviceBeans.add(adviceBean);}}if(!requestResponseBodyAdviceBeans.isEmpty()){this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);}...}publicstaticList<ControllerAdviceBean>findAnnotatedBeans(ApplicationContext context){ListableBeanFactory beanFactory = context;if(context instanceofConfigurableApplicationContext){
        beanFactory =((ConfigurableApplicationContext) context).getBeanFactory();}List<ControllerAdviceBean> adviceBeans =newArrayList<>();// 遍历所有的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(newControllerAdviceBean(name, beanFactory, controllerAdvice));}}}OrderComparator.sort(adviceBeans);return adviceBeans;}
3.3.2.2、HandlerMethodArgumentResolver方法参数解析器
  • 方法参数解析器有很多,差不多一个注解就对应一个参数解析器

在这里插入图片描述

3.3.2.3、HandlerMethodReturnValueHandler返回值处理器
  • 返回值处理器也有很多,我们只研究熟悉常用的就可以

在这里插入图片描述

对应方法参数解析器和返回值处理器这里就不展开说了,只要知道项目启动会加载这些东西即可,下一篇文章请求的执行流程再进入源码研究。

3.4、HandlerExceptionResolver异常处理器

  • 一句话解释它:Hander异常抛错后调用的方法

**与消息转换器很像,先遍历所有WebMvcConfigurer实现类configureHandlerExceptionResolvers中

新增

的异常处理器,如果没有开发者或者第三方新增,那么添加默认的的异常处理器,再调用扩展方法,也就是遍历所有WebMvcConfigurer实现类,调用他们的extendHandlerExceptionResolvers方法,对异常处理器做最后

修改

。**

// WebMvcConfigurationSupport类方法@BeanpublicHandlerExceptionResolverhandlerExceptionResolver(@Qualifier("mvcContentNegotiationManager")ContentNegotiationManager contentNegotiationManager){// 创建空集合List<HandlerExceptionResolver> exceptionResolvers =newArrayList<>();// 这里就是调用DelegatingWebMvcConfiguration重写的方法// 其实就是寻找WebMvcConfigurer实现类是否添加异常处理器configureHandlerExceptionResolvers(exceptionResolvers);if(exceptionResolvers.isEmpty()){addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}// 这里就是调用DelegatingWebMvcConfiguration重写的方法,扩展异常处理器extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite =newHandlerExceptionResolverComposite();
    composite.setOrder(0);
    composite.setExceptionResolvers(exceptionResolvers);return composite;}
  • ExceptionHandlerExceptionResolver:这个异常处理器内包含所有的自定义异常处理器- 因为这个对象不是bean对象,这里手动调用初始化接口的方法afterPropertiesSet
  • 最后添加默认异常处理器DefaultHandlerExceptionResolver
protectedfinalvoidaddDefaultHandlerExceptionResolvers(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(newJsonViewResponseBodyAdvice()));}if(this.applicationContext !=null){
        exceptionHandlerResolver.setApplicationContext(this.applicationContext);}// 手动调用初始化接口的初始化方法
    exceptionHandlerResolver.afterPropertiesSet();
    exceptionResolvers.add(exceptionHandlerResolver);// ResponseStatus异常处理器ResponseStatusExceptionResolver responseStatusResolver =newResponseStatusExceptionResolver();
    responseStatusResolver.setMessageSource(this.applicationContext);
    exceptionResolvers.add(responseStatusResolver);// 默认异常处理器
    exceptionResolvers.add(newDefaultHandlerExceptionResolver());}
3.4.1、加载自定义异常处理器
// ExceptionHandlerExceptionResolver类方法@OverridepublicvoidafterPropertiesSet(){// 初始化异常处理增强类initExceptionHandlerAdviceCache();if(this.argumentResolvers ==null){List<HandlerMethodArgumentResolver> resolvers =getDefaultArgumentResolvers();this.argumentResolvers =newHandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if(this.returnValueHandlers ==null){List<HandlerMethodReturnValueHandler> handlers =getDefaultReturnValueHandlers();this.returnValueHandlers =newHandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
  • 筛选异常处理器类:@ControllerAdvice类且方法上存在@ExceptionHandler
// ExceptionHandlerExceptionResolver类方法privatevoidinitExceptionHandlerAdviceCache(){if(getApplicationContext()==null){return;}// 获取所有@ControllerAdvice注解的bean,创建成ControllerAdviceBean,添加到集合中List<ControllerAdviceBean> adviceBeans =ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for(ControllerAdviceBean adviceBean : adviceBeans){Class<?> beanType = adviceBean.getBeanType();if(beanType ==null){thrownewIllegalStateException("Unresolvable type for ControllerAdviceBean: "+ adviceBean);}// 筛选方法上@ExceptionHandler注解ExceptionHandlerMethodResolver resolver =newExceptionHandlerMethodResolver(beanType);if(resolver.hasExceptionMappings()){this.exceptionHandlerAdviceCache.put(adviceBean, resolver);}if(ResponseBodyAdvice.class.isAssignableFrom(beanType)){this.responseBodyAdvice.add(adviceBean);}}}// ExceptionHandlerMethodResolver类属性和方法publicstaticfinalMethodFilterEXCEPTION_HANDLER_METHODS= method ->AnnotatedElementUtils.hasAnnotation(method,ExceptionHandler.class);publicExceptionHandlerMethodResolver(Class<?> handlerType){for(Method method :MethodIntrospector.selectMethods(handlerType,EXCEPTION_HANDLER_METHODS)){for(Class<?extendsThrowable> exceptionType :detectExceptionMappings(method)){addExceptionMapping(exceptionType, method);}}}

至此,SpringMVC启动重要的组件都介绍完毕。

总结

  • 加载继承AbstractAnnotationConfigDispatcherServletInitializer的MVC配置类(开发者创建,代替web.xml)
  • 既然代替web.xml那么这个配置类可以设置DispatcherServlet的映射路径注册过滤器
  • 父类AbstractAnnotationConfigDispatcherServletInitializer里面会创建web注解容器创建DispatcherServlet对象添加过滤器到Tomcat容器的过滤器集合中- DispatcherServlet初始化触发了web容器的刷新,加载所有@Controller注解的bean
  • 如果开发者或者第三方想要配置拦截器消息转换器的等配置,只要实现WebMvcConfigurer接口重写对应方法即可
  • 解析@RequestMapping注解- 遍历所有的bean,获取类上是否有@Controller@RequestMapping注解的bean- 再遍历所有的方法,筛选方法上是否有@RequestMapping注解- 最后包装成map存起来- key为@RequestMapping注解属性组成的RequestMappingInfo- value为Controller里存在@RequestMapping注解的Method组成的HandlerMethod
  • 解析@ControllerAdvice注解- 遍历所有的bean,筛选类上@ControllerAdvice注解的bean- 如果bean实现接口RequestBodyAdviceResponseBodyAdvice,那就是请求响应数据增强器- 如果bean有方法存在@ExceptionHandler,那就是异常处理器
标签: java spring mvc

本文转载自: https://blog.csdn.net/qq_35512802/article/details/134139936
版权归原作者 冬天vs不冷 所有, 如有侵权,请联系我们删除。

“SpringMVC源码解析(一):web容器启动流程”的评论:

还没有评论