美文网首页
WebMvcConfigurer 与 WebMvcConfigu

WebMvcConfigurer 与 WebMvcConfigu

作者: coding400 | 来源:发表于2019-07-22 15:39 被阅读0次

    1.WebMvcConfigurationSupport

    该类提供了主要的 MVC 配置方法,通过直接继承 WebMvcConfiguration ,并在继承类上 加上 @EnableWebMvc 和 @Configuration 注解之后。便可以在子类中实现父类的方法 ,更甚至可以实现含有@Bean 注解的方法,但记得一定要在实现的方法上加上 @Bean

    WebMvcConfigurationSupport 中提供了一些内容为空的方法,这些方法主要是用来给子类自定义时实现,根据方法名称的分类,有以下几种类型

    1. addXxx()
      为 Xxx 类型的处理器添加用户定义的处理器
      2.configureXxx()
      重新配置 Xxx 类型的处理器
      3.extendXxx()
      扩展 Xxx 类型的处理器,该方法一般放在 addXxx 和 ConfigureXxx 后面
      4.getXxx()
      将 Xxx 处理器类完全由子类控制
      示例:
    @Configuration
    public class WebConfiguration extends WebMvcConfigurationSupport {
        @Autowired
        private AuthorizationInterceptor authorizationInterceptor;
        @Autowired
        private SortHandlerMethodArgumentResolver sortHandlerMethodArgumentResolver;
        @Autowired
        private PageableHandlerMethodArgumentResolver pageableHandlerMethodArgumentResolver;
        @Autowired
        private StringHttpMessageConverter stringHttpMessageConverter;
        @Autowired
        private MappingJackson2HttpMessageConverter httpMessageConverter;
        /**
         * 拦截器配置
         *
         * @param registry 注册类
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authorizationInterceptor)
                    .addPathPatterns(WebConstants.API_PREFIX + "/**")
                    .addPathPatterns(WebConstants.WEB_PREFIX + "/**");     
        }
    
        /**
         * 静态资源
         * swagger + 前端
         * @param registry 
         */
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("swagger-ui.html","index.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
            registry.addResourceHandler("/static/**").addResourceLocations("classpath:/META-INF/resources/static/");
        }
    
        @Override
        protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(pageableHandlerMethodArgumentResolver);
            argumentResolvers.add(sortHandlerMethodArgumentResolver);
        }
    
        /**
         * 添加转换器
         */
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            for (int i = 0; i < converters.size(); i++) {
                if (converters.get(i) instanceof StringHttpMessageConverter){
                    converters.set(i, stringHttpMessageConverter);
                }
                if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                    converters.set(i, httpMessageConverter);
                }
            }
        }
    }
    
    

    2.WebMvcConfigurer

    该类是通过 "回调" 的方式来进行自定义化 Spring MVC 相关配置,大多数情况下,我们通过实现它的 抽象类 WebMvcConfigurerAdapter 来配置,因为它有WebMvcConfigurer 接口所有方法的一个空的实现,WebMvcConfigurer 接口中的所有方法都是 WebMvcConfigurationSupport 中提供给子类实现的空方法。

    为什么是说它是通过回调来实现的自定义配置? 用DelegatingWebMvcConfiguration 和 WebMvcConfigurerComposite 类来解释。

    DelegatingWebMvcConfiguration 类 简称【代理类】通过继承 WebMvcConfigurationSupport,所以拥有了 customize 自定义mvc 配置的权利。该类通过持有的对象 WebMvcConfigurerComposite 简称【聚合类】则是一个聚合了所有实现了 WebMvcConfigurer 接口的子类,如此一来,【代理类】所有实现 WebMvcConfigurationSupport 的方法调用时,则是将【聚合类】中的所有WebMvcConfiguer 接口的实现类的该方法依次进行调用

    class WebMvcConfigurerComposite implements WebMvcConfigurer {
            // 持有应用程序中所有实现 WebMvcConfigurer 接口的子类的一个集合
        private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();
    
           //
        public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
            if (!CollectionUtils.isEmpty(configurers)) {
                this.delegates.addAll(configurers);
            }
        }
    
            // 被父类调用的回调方法,来自 customize 各个子类的配置
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            for (WebMvcConfigurer delegate : this.delegates) {
                delegate.configurePathMatch(configurer);
            }
        }
            ......
    

    3.踩坑指南

    WebMvcConfigurationSupport 在整个应用程序中只会生效一个,如果用户已经实现了 WebMvcConfigurationSupport,则 DelegatingWebMvcConfiguration 将不会生效,换句话来说,WebMvcConfigurer 的所有实现类将不会生效。而在Spring 中,如果类路径上不存在 WebMvcConfigurationSupport 的实例,则将会默认实现
    WebMvcConfigurerAdapter、DelegatingWebMvcConfiguration 来自定义mvc 配置。

    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
            WebMvcConfigurerAdapter.class })
    // 类路径 不存在WebMvcConfigurationSupport实例才加载
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
            ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    
      //......
    }
    
    // 实现 adapter 
    @Configuration
        @Import(EnableWebMvcConfiguration.class)
        @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
        public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
    
            private static final Log logger = LogFactory
                    .getLog(WebMvcConfigurerAdapter.class);
    
            private final ResourceProperties resourceProperties;
    
            private final WebMvcProperties mvcProperties;
    
            private final ListableBeanFactory beanFactory;
    
            private final HttpMessageConverters messageConverters;
    
            final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
    
            public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
                    WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
                    HttpMessageConverters messageConverters,
                    ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
                this.resourceProperties = resourceProperties;
                this.mvcProperties = mvcProperties;
                this.beanFactory = beanFactory;
                this.messageConverters = messageConverters;
                this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
                        .getIfAvailable();
            }
      //......
    }
    
    //实现代理类,进行自定义化
    @Configuration
        public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    
            private final WebMvcProperties mvcProperties;
    
            private final ListableBeanFactory beanFactory;
    
            private final WebMvcRegistrations mvcRegistrations;
    
            public EnableWebMvcConfiguration(
                    ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
                    ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
                    ListableBeanFactory beanFactory) {
                this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
                this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
                this.beanFactory = beanFactory;
            }
    //......
    }
    

    而当 WebMvcAutoConfiguration 不生效时会导致以下几个问题:
    1.WebMvcProperties 和 ResourceProperties 失效

    因为两个配置类中的属性都在 WebMvcAutoConfiguration 中使用

    2.类路径上的 HttpMessageConverter 失效

    如:StringHttpMessageConverterConfiguration、MappingJackson2HttpMessageConverter ,因为 HttpMessageConverters 中持有着所有HttpMessageConverter的实例, 在 WebMvcAutoConfigurationAdapter 中会注入 HttpMessageConverters ,因此当 WebMvcAutoConfigurationAdapter 不加载时,则会失效,间接的造成 spring.http.encoding.charset 与 spring.jackson.date-format 假象的失效。

    如:
    StringHttpMessageConverter 会使用 spring.http.encoding.charset 配置, 默认编码为:ISO-8859-1

            @Bean
            @ConditionalOnMissingBean
            public StringHttpMessageConverter stringHttpMessageConverter() {
                StringHttpMessageConverter converter = new StringHttpMessageConverter(
                        this.encodingProperties.getCharset());
                converter.setWriteAcceptCharset(false);
                return converter;
            }
    

    常用解决方案:
    因为已知的配置类都已通过 @Bean 注册在容器中,那么我们可以在使用 WebMvcConfigurationSupport 时,通过 @Autowired 注入到配置类中,然后替换调 WebMvcConfigurationSupport 默认的配置即可,如:

    @Configuration
    public class WebConfiguration extends WebMvcConfigurationSupport {
        @Autowired
        private StringHttpMessageConverter stringHttpMessageConverter;
        @Autowired
        private MappingJackson2HttpMessageConverter httpMessageConverter;
        /**
         * 添加转换器
         */
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            for (int i = 0; i < converters.size(); i++) {
                if (converters.get(i) instanceof StringHttpMessageConverter){
                    converters.set(i, stringHttpMessageConverter);
                }
                if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                    converters.set(i, httpMessageConverter);
                }
            }
        }
    }
    

    也可以根据需求来重新实现。总之明白为什么会失效之后,解决起来思路也更清晰

    相关文章

      网友评论

          本文标题:WebMvcConfigurer 与 WebMvcConfigu

          本文链接:https://www.haomeiwen.com/subject/kjcelctx.html