一次跨域配置记录@EnableWebMvc、WebMvcConfigurationSupport、WebMvcConfigurer、DelegatingWebMvcConfiguration

问题引出

实现跨域配置时,导致YML的自动加载注解失效(WebMvcAutoConfiguration此注解失效)
问题一

@Configuration
//此注解有坑
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

问题二

@Configuration
public class WebConfiguration extends DelegatingWebMvcConfiguration{
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

问题三

@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport{
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}

前置知识

@Import:即在含有该注解的类中导入代码中相应的类型的bean(DelegatingWebMvcConfiguration)并在Springboot启动时进行加载其类中实例,总结:实例化并加载导入的类(导入子类也会实例化父类)

@Import(DelegatingWebMvcConfiguration.class)

@ConditionalOnMissingBean:当WebMvcConfigurationSupport类型的bean不存在时加载含有该注解的类

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

@EnableWebMvc

EnableWebMvc是什么?
直接看源码,@EnableWebMvc实际上引入一个DelegatingWebMvcConfiguration。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
...
}

所以@EnableWebMvc=继承DelegatingWebMvcConfiguration=继承WebMvcConfigurationSupport


@WebMvcAutoConfiguration

由@EnableAutoConfiguration注解引出@WebMvcAutoConfiguration

首先我们要搞清楚@EnableAutoConfiguration的设置到底代表的是啥,从自动装配的原理入手,我们翻spring.factories,和mvc有关的自动装配类,是如下几个:

org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\

注意最后一行,凭感觉,其中WebMvcAutoConfiguration一定和Spring MVC在Spring boot中自动装配最有关,找下去,这是class的声明。

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
...
}

这是因为在 springboot的web自动配置类 WebMvcAutoConfiguration 上有条件注解
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

这个注解的意思是在项目类路径中缺少WebMvcConfigurationSupport类型的bean时改自动配置类才会生效,所以继承 WebMvcConfigurationSupport 后需要自己再重写相应的方法。


1.可以看到,当WebMvcConfigurationSupport类不存在的时候,该自动装配类才会创建出来,也就是说,如果我们使用@EnableWebMvc,@EnableWebMvc就相当于导入了WebMvcConfigurationSupport类(前面介绍@EnableWebMvc有提到),这个时候,spring boot的自动装配就不会发生了,这个时候,我们能用的,只有WebMvcConfigurationSupport提供的若干个配置

2.至此,可以说明,无论是使用第一种方法@EnableWebMvc还是第二种方法扩展DelegatingWebMvcConfiguration类及第三种方法扩展WebMvcConfigurationSupport类,spring都会创建一个WebMvcConfigurationSupport的类,进而屏蔽掉自动装配类WebMvcAutoConfiguration

那么为何扩展WebMvcConfigurationAdapter(Java8 有接口默认方法后,该类被废弃,转为使用WebMvcConfigurer接口,下文我们统一用WebMvcConfigurer)不会屏蔽掉spring boot的自动装配呢?

这要再说WebMvcConfigurer的工作方式了,实际上,使用@EnableWebMvc引入的类不是WebMvcConfigurationSupport,而是DelegatingWebMvcConfiguration
DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport,并且DelegatingWebMvcConfiguration通过依赖注入,持有Spring 容器所有WebMvcConfigurer实现类的一个List,所有对spring mvc的自定义的WebMvcConfigurer,都会挂到DelegatingWebMvcConfiguration上去。

WebMvcAutoConfiguration内部,创建了一个继承了DelegatingWebMvcConfiguration的内部类EnableWebMvcConfiguration,这个类,一方面,提供了缺失的WebMvcConfigurationSupport的功能,另一方面,就起到了收集所有WebMvcConfigurer,调用它们的方法的作用。


最后我们对前面2点总结

  • 无论是使用@EnableWebMvc还是WebMvcConfigurationSupport,都会禁止Springboot的自动装配,只有使用WebMvcConfigurer才不会。
  • 虽然禁止了Springboot的自动装配,但是WebMvcConfigurationSupport本身,还是会注册一系列的MVC相关的bean的,从附加的api可以看到。
  • 后面第2点可以得出WebMvcAutoConfiguration自动装备,其实会创建一个WebMvcConfigurationSupport的子类,叫EnableWebMvcConfiguration

总结

  1. implements WebMvcConfigurer :不会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置
  2. @EnableWebMvc + implements WebMvcConfigurer :会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置(问题引出第一种方法
  3. extends DelegatingWebMvcConfiguration:会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置(问题引出第二种方法
  4. extends WebMvcConfigurationSupport:会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置(问题引出第三种方法

如果有配置文件继承了DelegatingWebMvcConfiguration,或者WebMvcConfigurationSupport,或者配置文件有@EnableWebMvc,那么 @EnableAutoConfiguration 中的WebMvcAutoConfiguration 将不会被自动配置,而是使用WebMvcConfigurationSupport及自己定义的配置。


额外的一些

细心的开发者会发现,WebMvcConfigurationSupport中那些子类可以重写的空方法在WebMvcConfigurer都有,这说明WebMvcConfigurer只是WebMvcConfigurationSupport的一个扩展类,它并没有扩展新功能,只是为让用户更方便安全的添加自定义配置,为什么说是安全呢?因为如果直接继承WebMvcConfigurationSupport,那么用户可以重写默认的配置,如果对原理不是很清楚地开发者不小心重写了默认的配置,springmvc可能相关功能就无法生效,是一种不安全的行为。
1.为什么WebMvcConfigurer实现要加@EnableWebMvc?
@EnableWebMvc注解类上导入了DelegatingWebMvcConfiguration类,该类是WebMvcConfigurationSupport的子类,该类除了实例化WebMvcConfigurationSupport实例以外,另一个作用就是收集BeanFactory中所有WebMvcConfigurer的实现,汇集到WebMvcConfigurerComposite中,在WebMvcConfigurationSupport实例化过程中会分别调用这些实现,将相应的实例传入这些实现中,供开发者在此基础上添加自定义的配置。这也就是在WebMvcConfigurer子类上要加@EnableWebMvc的原因,因为要先实例化WebMvcConfigurationSupport。
2.为什么可以存在多个WebMvcConfigurer的实现?
一般来讲一个应用中一个WebMvcConfigurer的已经足够,设计成收集多个是不是有些多余?从springboot的autoconfigure机制来看并不多余,反而更灵活,比如我要写一个mybatis的AutoConfiguration和JPA的AutoConfiguration,我就可以在不同的AutoConfiguration里面定义一个WebMvcConfigurer的实现,里面只配置与mybatis或JPA相关的配置,这样需要那个启用那个,不需要人工通过注释代码来转换mybatis和JPA,注意:在springboot下自定义的WebMvcConfigurer实现配置类上是不需要添加@EnableWebMvc的,因为springboot已经实例化了WebMvcConfigurationSupport,如果添加了该注解,默认的WebMvcConfigurationSupport配置类是不会生效的,也就是以用户定义的为主,一般建议还是不覆盖默认的好。


解决问题引出三种方法

@EnableWebMvc此注解有坑,注释掉即可

@Configuration
//此注解有坑,注释掉即可
//@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值