发布时间:2025-06-24 19:33:10  作者:北方职教升学中心  阅读量:235


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(){// 字符编码过滤器CharacterEncodingFiltercharacterEncodingFilter =newCharacterEncodingFilter();characterEncodingFilter.setEncoding("UTF-8");characterEncodingFilter.setForceEncoding(true);// HiddenHttpMethodFilter 用于支持 PUT、创建DispatcherServlet对象
  • 1.3、

    // WebMvcConfigurationSupport类方法protectedfinalvoidaddDefaultHttpMessageConverters(List<HttpMessageConverter<?>>messageConverters){// 默认消息转换器messageConverters.add(newByteArrayHttpMessageConverter());messageConverters.add(newStringHttpMessageConverter());messageConverters.add(newResourceHttpMessageConverter());messageConverters.add(newResourceRegionHttpMessageConverter());...// jacksonif(jackson2Present){Jackson2ObjectMapperBuilderbuilder =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、实例化
    • 3.3.1.1、RequestMappingInfo、ResponseBodyAdvice)
    • 3.3.2.2、HandlerMethodArgumentResolver方法参数解析器
      publicinterfaceHandlerMethodArgumentResolver{/**	 * 此解析器是否支持给定的方法参数	 */booleansupportsParameter(MethodParameterparameter);/**	 * 将方法参数解析为给定请求的参数值	 */@NullableObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException;}
      • 方法参数解析器有很多,差不多一个注解就对应一个参数解析器

      在这里插入图片描述

      3.3.2.3、

      // WebMvcConfigurerComposite类classWebMvcConfigurerCompositeimplementsWebMvcConfigurer{privatefinalList<WebMvcConfigurer>delegates =newArrayList<>();@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){for(WebMvcConfigurerdelegate :this.delegates){delegate.addInterceptors(registry);}}@OverridepublicvoidaddCorsMappings(CorsRegistryregistry){for(WebMvcConfigurerdelegate :this.delegates){delegate.addCorsMappings(registry);}}@OverridepublicvoidconfigureViewResolvers(ViewResolverRegistryregistry){for(WebMvcConfigurerdelegate :this.delegates){delegate.configureViewResolvers(registry);}}@OverridepublicvoidconfigureMessageConverters(List<HttpMessageConverter<?>>converters){for(WebMvcConfigurerdelegate :this.delegates){delegate.configureMessageConverters(converters);}}}

      3、HandlerExceptionResolver异常处理器

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

        与消息转换器很像,先遍历所有WebMvcConfigurer实现类configureHandlerExceptionResolvers中新增的异常处理器,如果没有开发者或者第三方新增,那么添加默认的的异常处理器,再调用扩展方法,也就是遍历所有WebMvcConfigurer实现类,调用他们的extendHandlerExceptionResolvers方法,对异常处理器做最后修改之前文章Spring源码解析(四):单例bean的创建流程有介绍过,bean对象创建后会调用各种初始化方法,其实就包括调用InitializingBean接口afterPropertiesSet方法来实现初始化。获取所有跨域配置

  • 3.2.2、

    在这里插入图片描述

    • 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、如果开发者和第三方都没有添加,那么设置默认的消息转换器,设置完以后,再调用扩展方法,也就是遍历所有WebMvcConfigurer实现类,调用他们的extendMessageConverters方法,对消息转换器做最后修改。SpringServletContainerInitializer
    • 1、WebMvcConfigurer接口

        在讲DelegatingWebMvcConfiguration之前先说下WebMvcConfigurer接口,因为下面内容都是围绕着WebMvcConfigurer接口展开的。加载自定义异常处理器

      // 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(ControllerAdviceBeanadviceBean :adviceBeans){Class<?>beanType =adviceBean.getBeanType();if(beanType ==null){thrownewIllegalStateException("Unresolvable type for ControllerAdviceBean: "+adviceBean);}// 筛选方法上@ExceptionHandler注解ExceptionHandlerMethodResolverresolver =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(Methodmethod :MethodIntrospector.selectMethods(handlerType,EXCEPTION_HANDLER_METHODS)){for(Class<?extendsThrowable>exceptionType :detectExceptionMappings(method)){addExceptionMapping(exceptionType,method);}}}

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

      在这里插入图片描述

      1、SimpleUrlHandlerMapping

  • 适配器HandlerAdapter

    publicinterfaceHandlerAdapter{/**	 * 因为有多个HandlerMapping和HandlerAdapter	 * 对于HandlerAdapter是否支持对应的HandlerMapping,通过此方法判断	 */booleansupports(Objecthandler);/**	 * 具体调用Hangder的方法	 */@NullableModelAndViewhandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException;}
    • 上一步中不同映射器通过请求映射到的Handler不确定类型
      • @RequestMapping注解方式获取到的是具体的Method
      • 其他实现接口方式获取到的是具体的Class
    • 此时拿到Handler去执行具体的方法时候,方式是不统一
    • 那么适配器就出现了
    • RequestMappingHandlerAdapter就是处理@RequestMapping注解的Handler

    3.2、StandardWrapper中有介绍。web容器初始化入口
  • 2、跨域配置等@OverrideprotectedvoidconfigureDefaultServletHandling(DefaultServletHandlerConfigurerconfigurer){this.configurers.configureDefaultServletHandling(configurer);}@OverrideprotectedvoidaddInterceptors(InterceptorRegistryregistry){this.configurers.addInterceptors(registry);}@OverrideprotectedvoidaddCorsMappings(CorsRegistryregistry){this.configurers.addCorsMappings(registry);}@OverrideprotectedvoidconfigureViewResolvers(ViewResolverRegistryregistry){this.configurers.configureViewResolvers(registry);}@OverrideprotectedvoidconfigureMessageConverters(List<HttpMessageConverter<?>>converters){this.configurers.configureMessageConverters(converters);}@OverrideprotectedvoidconfigureHandlerExceptionResolvers(List<HandlerExceptionResolver>exceptionResolvers){this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);}...}
  •   调用DelegatingWebMvcConfiguration重写的MVC配置方法实际就是对应的配置添加到对应的注册器中。HandlerMethodReturnValueHandler返回值处理器

  • 3.4、初始化

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

      @RequestMapping注解肯定是在容器启动时候解析的,那么这个工作就放在RequestMappingHandlerMapping这个bean对象的初始化阶段来完成。获取候选bean

  • 3.2.2.2、RequestMappingHandlerMapping映射器
    • 一句话解释它:解析@RequestMapping注解
    3.2.1、通过实现WebMvcConfigurer接口,可以定制化SpringMVC的配置例如添加拦截器、方法参数解析器、获取消息转换器
  • 3.3.2、DispatcherServlet初始化

    DispatcherServlet类图如下:

    在这里插入图片描述

      Tomcat启动容器加载Servelt(这里是DispatcherServlet)并初始化,就会调用到这里。一般我们会导入Jackson,那么这里会添加MappingJackson2HttpMessageConverter消息转换器。注册过滤器

  • 2、AbstractDispatcherServletInitializer注册前端控制器
    • 1.1、

      // WebMvcConfigurationSupport类方法@BeanpublicHandlerExceptionResolverhandlerExceptionResolver(@Qualifier("mvcContentNegotiationManager")ContentNegotiationManagercontentNegotiationManager){// 创建空集合List<HandlerExceptionResolver>exceptionResolvers =newArrayList<>();// 这里就是调用DelegatingWebMvcConfiguration重写的方法// 其实就是寻找WebMvcConfigurer实现类是否添加异常处理器configureHandlerExceptionResolvers(exceptionResolvers);if(exceptionResolvers.isEmpty()){addDefaultHandlerExceptionResolvers(exceptionResolvers,contentNegotiationManager);}// 这里就是调用DelegatingWebMvcConfiguration重写的方法,扩展异常处理器extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverCompositecomposite =newHandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);returncomposite;}
      • ExceptionHandlerExceptionResolver:这个异常处理器内包含所有的自定义异常处理器
        • 因为这个对象不是bean对象,这里手动调用初始化接口的方法afterPropertiesSet
      • 最后添加默认异常处理器DefaultHandlerExceptionResolver
      protectedfinalvoidaddDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver>exceptionResolvers,ContentNegotiationManagermvcContentNegotiationManager){// 这个异常处理器内包含所有的自定义异常处理器ExceptionHandlerExceptionResolverexceptionHandlerResolver =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异常处理器ResponseStatusExceptionResolverresponseStatusResolver =newResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);// 默认异常处理器exceptionResolvers.add(newDefaultHandlerExceptionResolver());}
      3.4.1、SpringServletContainerInitializer的作用解析
  • 三、@EnableWebMvc解析

    @EnableWebMvc

    在这里插入图片描述

    1、

    // WebMvcConfigurationSupportprotectedfinalMap<String,CorsConfiguration>getCorsConfigurations(){if(this.corsConfigurations ==null){CorsRegistryregistry =newCorsRegistry();addCorsMappings(registry);this.corsConfigurations =registry.getCorsConfigurations();}returnthis.corsConfigurations;}

    3.2.2、过滤器等
  • WebAppInitalizer即为WebApplicationInitializer的实现类,也就是SpringServletContainerInitializer要找的感兴趣的类,获取到多个放到集合initializer中,然后排序,最后遍历调用onStartup方法
  • 在这里插入图片描述

      总结SpringServletContainerInitializer作用:加载自定义的WebApplicationInitializer初始化核心接口的实现类WebAppInitializer,调用onStartup方法来实现web容器初始化。

      接下来的入口就在自定义g配置类SpringMVCConfi这里,因为它的配置类注解@Configuration(也是@Component),@ComponentScan(扫描@Controller注解)@EnableWebMvc(导入DelegatingWebMvcConfiguration.class)注解都会被扫描解析到。测试Controller

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

    启动tomcat发送请求结果

    在这里插入图片描述

    二、DelegatingWebMvcConfiguration

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

    • 如果开发者或者第三方想要配置拦截器、RequestMappingHandlerMapping映射器
      • 3.2.1、DispatcherServlet初始化
    • 四、

      在这里插入图片描述

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

      在这里插入图片描述

      • 容器刷新抽象类AbstractApplicationContextrefresh方法,看过spring源码的应该很熟悉
      • web容器spring容器都间接继承了AbstractApplicationContext,容器刷新都调用如下方法
      • 关于spring的源码Spring源码解析(三):bean容器的刷新之前介绍

      在这里插入图片描述

        容器初始化时候有个很重要的bean工厂后置处理器ConfigurationClassPostProcessor,作用就是解析@Configuration,@Import,@ComponentScan,@Bean等注解给bean容器添加bean定义,之前文章Spring源码解析(六):bean工厂后置处理器ConfigurationClassPostProcessor有介绍。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、添加过滤器到Tomcat容器的过滤器集合中
      • DispatcherServlet初始化触发了web容器的刷新,加载所有@Controller注解的bean

    • 如果开发者或者第三方想要配置拦截器、加载自定义异常处理器
  • 总结
  • 一、初始化
    • 3.2.2.1、RequestMappingHandlerAdapter适配器
      • 3.3.1、返回值处理、创建web注解容器
      • 1.2、获取所有拦截器
      • 3.2.1.2、消息转换器的等配置,只要实现WebMvcConfigurer接口重写对应方法即可
      • 解析@RequestMapping注解(根据注解属性创建对象RequestMappingInfo)
        • 遍历所有的bean,获取类上是否有@Controller@RequestMapping注解的bean
        • 再遍历所有的方法,筛选方法上是否有@RequestMapping注解
        • 最后注册成两个map存起来,以后映射方法从这里获取
          • MappingRegistry#MultiValueMap<String, T> pathLookup
            • key为@RequestMapping注解的映射路径
            • value为RequestMappingInfo对象
          • MappingRegistry#Map<T, MappingRegistration> registry
            • key为RequestMappingInfo对象
            • value为MappingRegistration对象(包含Controller#Method)
      • 解析@ControllerAdvice注解
        • 遍历所有的bean,筛选类上@ControllerAdvice注解的bean
          • 如果bean实现接口RequestBodyAdviceResponseBodyAdvice,那就是请求响应数据增强器
          • 如果bean有方法存在@ExceptionHandler,那就是异常处理器