spring mvc之注解@EnableWebMvc
Posted 不识君的荒漠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring mvc之注解@EnableWebMvc相关的知识,希望对你有一定的参考价值。
前言
其实我接触Java web开发比较晚,这句话的意思就是,我做开发的时候就使用的是比较新的技术了,比如spring boot,从来没用过ssh那一套,虽然用了spring mvc,但也是基于spring boot封装好的。
当然了,这有好处,也有坏处,好处是跟上了时代的潮流,坏处是对于被封装的那一套了解不够深刻。
今天在翻某些框架封装的源码时候,看到一些WEB项目的配置类继承了WebMvcConfigurerAdapter类,然后自定义了一些配置。另外在这个类还有这个注解:@EnableWebMvc。
然后产生了两个疑问:
1. 想要自定义spring mvc的配置为什么继承WebMvcConfigurerAdapter类
2. @EnableWebMvc注解起什么用
带着这两个疑问,准备一探究竟
1. 为什么要继承WebMvcConfigurerAdapter
首先说明下,spring mvc有几个核心组件,这些组件的配置都是可扩展的。这几个核心组件不是本文的重点,也不做强调,需要了解的可以查阅相关资料。我以前看过《看透Spring MVC源代码分析与实践》作者:韩路彪,这本书中讲解了spring mvc的九大组件,感兴趣的可以看下。
那么问题来了,如果想自定义这些配置,为什么要继承WebMvcConfigurerAdapter类。查看了其中一些类的注释,并不是一定要继承这个类,但是如果想自定义一些高级配置,建议继承它,什么算高级?嘿嘿。
先介绍下这个类,这个类实现了WebMvcConfigurer接口的所有方法(都是空实现),这里提一下WebMvcConfigurer接口,类的注释上是这样说明的:
定义回调方法,以通过@code @EnableWebMvc自定义启用Spring MVC的基于Java的配置。
@code @EnableWebMvc已注释的配置类可以实现此接口的回调,并有机会自定义默认配置。 考虑扩展@link WebMvcConfigurerAdapter,它提供所有接口方法的存根实现。
这几句注释在我看来有下面几个意思:
1. 使用@EnableWebMvc注解启用spring mvc的基于java config的配置
2. 实现WebMvcConfigurer接口的方法可以自定义spring mvc的配置
3. 对于第2个意思,建议采用继承WebMvcConfigurerAdapter类来实现
4. 如果想要让继承WebMvcConfigurerAdapter的自定义配置的子类起作用,那这个类应该是配置类(比如加上注解@Configuration,毕竟这个类应该托管到spring 容器内,spring mvc才会知道这个子类,要不这些自定义配置怎么起作用)
也就是说,想要启用spring mvc的时候,应用使用注解@EnableWebMvc启用spring mvc的配置,另外,如果想自定义这些配置,就使用一个可以托管到spring容器的配置类,继承WebMvcConfigurerAdapter类并重写需要自定义配置的那些方法。
到这里, 我又产生了几个疑问:
Q1. 我继承了WebMvcConfigurerAdapter类并重写需要自定义配置的那些方法,但是spring mvc是怎么知道的
Q2. spring mvc怎么知道我要自定义哪些配置,我自定义的配置会不会导致默认配置不可用
Q3. 这个自定义配置的子类是怎么和spring mvc关联的
这些问题的答案,应该就在@EnableWebMvc注解这里。所以,看下文
2. @EnableWebMvc注解起什么用
@EnableWebMvc注解起什么用?先看下源码中第一行的注解说明:
将此注解添加到@code @Configuration类可从@link WebMvcConfigurationSupport导入Spring MVC配置
也就是说,这个注解应当加到有@Configuration注解的类上(意思是这个类应当是托管到spring容器的配置类),然后就可以从从@link WebMvcConfigurationSupport导入Spring MVC配置,问题在这个WebMvcConfigurationSupport类上。
再看下@EnableWebMvc注解类的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc
重点在于:@Import(DelegatingWebMvcConfiguration.class)这里,这是基于java config格式的配置类的导入,然后看下DelegatingWebMvcConfiguration的源码:
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
那就明白了:DelegatingWebMvcConfiguration是继承了WebMvcConfigurationSupport的配置类。
现在先来看下WebMvcConfigurationSupport是什么,注释是这样解释的:
这是提供MVC Java配置背后的配置的主类。 通常通过将@link EnableWebMvc @EnableWebMvc添加到应用程序@link Configuration @Configuration类来导入它。 另一种选择
高级选项是直接从此类扩展并根据需要覆盖方法,记住将@link Configuration @Configuration添加到子类和@link Bean @Bean以覆盖@link Bean @Bean方法。 有关更多详细信息,请参阅@link EnableWebMvc @EnableWebMvc的Javadoc。
这个WebMvcConfigurationSupport类的作用呢,其实提供了上文提到的spring mvc的几个核心组件的能力。如果想要从此类扩展,只需要继承并重写它的一些方法(有兴趣的可以看下这个类的源码)。
现在就是,DelegatingWebMvcConfiguration类继承了WebMvcConfigurationSupport类并重写了它的一些方法,并且DelegatingWebMvcConfiguration类是一个配置类被托管了spring容器,重点来了:
这里说明了@EnableWebMvc注解的一个作用:
1. 启用spring mvc的这几个核心组件提供的能力(就是启用了spring mvc)
如果还不明白,这里解释下:上文说了,@EnableWebMvc注解需要用在一个可以注册到spring容器的配置类上,然后@EnableWebMvc注解导入了DelegatingWebMvcConfiguration配置类,这个类继承了WebMvcConfigurationSupport类提供的spring mvc各个组件的能力并且这个类也被注册到了spring容器。
那么,@EnableWebMvc注解和自定义配置的关系在哪,我认为这算是@EnableWebMvc注解提供的第二个作用:
2. 支持自定义spring mvc配置的能力
而它的这个能力,关键在于DelegatingWebMvcConfiguration配置类,上文说了DelegatingWebMvcConfiguration类继承自WebMvcConfigurationSupport类并重写了它的一些方法,就是它重写的这些方法,允许了我们增加自定义配置。
看一下DelegatingWebMvcConfiguration类的部分源码,作个解释:
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 注意看这里,这里把spring容器的所有实现了WebMvcConfigurer接口的类的bean作为一个集合
// 变量注入到了这里。
// 这就意味着,我们需要自定义spring mvc配置的那些配置类,都会被注入到这里
// 这样就可以把所有配置(包括我们自定义的配置添加进去)
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers)
if (!CollectionUtils.isEmpty(configurers))
this.configurers.addWebMvcConfigurers(configurers);
@Override
protected void configurePathMatch(PathMatchConfigurer configurer)
this.configurers.configurePathMatch(configurer);
@Override
protected void configureContentNegotiation(ContentNegotiationConfigurer configurer)
this.configurers.configureContentNegotiation(configurer);
@Override
protected void configureAsyncSupport(AsyncSupportConfigurer configurer)
this.configurers.configureAsyncSupport(configurer);
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
this.configurers.configureDefaultServletHandling(configurer);
//...
不用太多,上面几行就明白了。
关键在于DelegatingWebMvcConfiguration类的这个属性上:
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 看下WebMvcConfigurerComposite类的部分源码:
//可以看到这个类有个属性delegates,是个WebMvcConfigurer接口的实现类的集合
class WebMvcConfigurerComposite implements WebMvcConfigurer
private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers)
if (!CollectionUtils.isEmpty(configurers))
this.delegates.addAll(configurers);
@Override
public void configurePathMatch(PathMatchConfigurer configurer)
for (WebMvcConfigurer delegate : this.delegates)
delegate.configurePathMatch(configurer);
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer)
for (WebMvcConfigurer delegate : this.delegates)
delegate.configureContentNegotiation(configurer);
//...
注意看下,我上面贴的两段代码中加的一些中文注释。结合起来一看,这样就明白了,DelegatingWebMvcConfiguration类会把所有实现了接口WebMvcConfigurer的类(子类也是,这是java语法,就不说了)包括我们那些托管到spring容器的自定义的配置类(因为也实现了它)都会把这些配置加上。
这也就是解释了第1节中提到的Q1、Q3的问题:我的配置类注册到了spring容器中,spring通过自动注入的方式把所有WebMvcConfigurer接口的实现类注入到了DelegatingWebMvcConfiguration的configurers属性中,在WebMvcConfigurerComposite类把这些配置都给配置上。然后回调那些实现了WebMvcConfigurer接口的实现类,最终将我们自定义的配置都给加上。
现在,就剩Q2这个问题了,spring mvc怎么知道我自定义哪些配置了,在WebMvcConfigurerComposite类回调我们重写方法的接口时,如果我们重写了需要自定义配置的方法,自然就加上了,现在的问题是第二个,如果自定义了配置,是否会加载默认配置?这个就看自定义谁的配置了,比如HttpMessageConverter,如果在重写了方法configMessageConverters自定义了配置,就不会加载默认配置,如果重写的方法是extendMessageContertes就会加载自定义的和默认的,看下源码就明白了:
/**
* Provides access to the shared @link HttpMessageConverters used by the
* @link RequestMappingHandlerAdapter and the
* @link ExceptionHandlerExceptionResolver.
* This method cannot be overridden.
* Use @link #configureMessageConverters(List) instead.
* Also see @link #addDefaultHttpMessageConverters(List) that can be
* used to add default message converters.
*/
protected final List<HttpMessageConverter<?>> getMessageConverters()
if (this.messageConverters == null)
this.messageConverters = new ArrayList<HttpMessageConverter<?>>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty())
// 这里如果非空的就不加载默认配置了,注释上也有解释
addDefaultHttpMessageConverters(this.messageConverters);
extendMessageConverters(this.messageConverters);
return this.messageConverters;
其它几个组件,有兴趣可以查阅相关资料,或者翻下源码了解下。
纯粹是自已瞎研究,有问题欢迎及时指正。
以上是关于spring mvc之注解@EnableWebMvc的主要内容,如果未能解决你的问题,请参考以下文章
Spring MVC之@RequestParam @RequestBody @RequestHeader 等详解