一、入口
SpringMVC自动配置的入口是WebMvcAutoConfiguration,声明如下:
1 | //声明此类是个配置类 |
简要分析下,WebMvcAutoConfiguration这个类只有在满足以下条件才会自动配置:
- 必须是web应用。而且是Servlet类型的
- 在classPath下,必须有Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class 这里个类
- 在容器中,不得存在WebMvcConfigurationSupport这个类以及子类
二、中间体分析
在WebMvcAutoConfiguration类中,中间体我们只看以下部分,剩下的在下一节给出:
- addResourceHandlers:这个方法是用来绑定静态资源的。从代码上我们可以知道在SpringBoot中,静态资源放在哪,我们才可以访问到。
1 | //静态资源路径 |
- addResourceHandlers总结:
- addResourceHandlers就是配置静态资源映射。主要是配置了如下几个:
- 所有/webjars/**的请求,都会去classPath下的/META-INF/resources/webjars下面去找。
- 所有的/**的请求,都会去classpath:/META-INF/resources/, classpath:/resources/,classpath:/static/, classpath:/public/下面去找。
- 根据以上结论,我们只需要把静态资源放到classpath:/META-INF/resources/, classpath:/resources/,classpath:/static/, classpath:/public/下面即可。而webjars本身就是把静态资源,如jquery.js打包成一个jar包形式,我们只需要在maven中引用下,就可以使用了。
- addResourceHandlers就是配置静态资源映射。主要是配置了如下几个:
- WelcomePageHandlerMapping:配置欢迎页
1 |
|
- WelcomePageHandlerMapping总结
- WelcomePageHandlerMapping是配置欢迎页,会遍历静态资源路径,寻找第一个名为index.html的页面为欢迎页。\
-
FaviconConfiguration:图标配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//需要开启spring.mvc.favicon.enabled这个配置,但是默认是开启的
"spring.mvc.favicon.enabled", matchIfMissing = true) (value =
public static class FaviconConfiguration implements ResourceLoaderAware {
private final ResourceProperties resourceProperties;
private ResourceLoader resourceLoader;
public FaviconConfiguration(ResourceProperties resourceProperties) {
this.resourceProperties = resourceProperties;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
//设置图标映射
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
//所有的**/favicon.ico都会映射到faviconRequestHandler()的路径上。也就是静态资源路径上
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
faviconRequestHandler()));
return mapping;
}
public ResourceHttpRequestHandler faviconRequestHandler() {
ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
requestHandler.setLocations(resolveFaviconLocations());
return requestHandler;
}
private List<Resource> resolveFaviconLocations() {
//静态资源映射
String[] staticLocations = getResourceLocations(
this.resourceProperties.getStaticLocations());
List<Resource> locations = new ArrayList<>(staticLocations.length + 1);
Arrays.stream(staticLocations).map(this.resourceLoader::getResource)
.forEach(locations::add);
locations.add(new ClassPathResource("/"));
return Collections.unmodifiableList(locations);
}
}- FaviconConfiguration总结:配置页面图标,所有的**/favicon.ico路径,都会去静态资源路径下去找。
-
beanNameResolver、viewResolver。也就是说SpringBoot帮我们自动配置了ContentNegotiatingViewResolver、BeanNameViewResolver视图解析器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public InternalResourceViewResolver defaultViewResolver() {// 默认的视图解析器是InternalResourceViewResolver
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
.class) (View
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
.class) (ViewResolver
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(
beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver uses all the other view resolvers to locate
// a view so it should have a high precedence
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
三、如何定制我们自己需要的SpringMVC组件
- SpringBoot帮我们配置了什么?
- Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 自动配置了两个视图解析器
- ContentNegotiatingViewResolver:组合所有的视图解析器的;
- 如何定制我们自己的?
- 往容器中加入一个视图解析器即可。SpringBoot会自动帮我们组合起来
- Support for serving static resources, including support for WebJars (see below).
- 配置好了静态资源路径以及webjars映射的路径
- Automatic registration of
Converter
,GenericConverter
,Formatter
beans.- 自动注册了转换器、格式器
- 如何定制?
- 在容器中添加一个自己的转换器或格式器即可。
- Support for
HttpMessageConverters
(see below).- HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User—Json;
- HttpMessageConverters 是从容器中确定;获取所有的HttpMessageConverter;
- 如何定制?
- 只需要在容器中添加一个自己的HttpMessageConverter。
- Automatic registration of
MessageCodesResolver
(see below).- 定义错误代码生成规则
- Static
index.html
support.- 静态首页访问
- Custom
Favicon
support (see below).- 图标访问
- Automatic use of a
ConfigurableWebBindingInitializer
bean (see below).
- Inclusion of
- 综上所述,如果需要定制我们自己需要的东西,可以直接在容器中添加相应的Bean即可。
四、如何扩展SpringMVC
-
官方文档上的原文:
If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own
@Configuration
class of typeWebMvcConfigurerAdapter
, but without@EnableWebMvc
. If you wish to provide custom instances ofRequestMappingHandlerMapping
,RequestMappingHandlerAdapter
orExceptionHandlerExceptionResolver
you can declare aWebMvcRegistrationsAdapter
instance providing such components. -
翻译出来的意识大概是这样的,如果我们需要扩展SpringMVC,需要做以下几点,这样就扩展了SpringMVC,并且自动配置的内容也还生效。即既保留了自动配置的,也有我们自己扩展的。相互合作。:
- 定义一个类,在类名上加上
Configuration
注解 - 该类继承
WebMvcConfigurerAdapter
在最新版本的SpringBoot中,已经不需要继承这个适配类,而是直接实现WebMvcConfigurer
- 如果你需要定制
RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
、ExceptionHandlerExceptionResolver
,需要声明一个WebMvcRegistrationsAdapter
实例。 - ==不能再类名上加上
@EnableWebMvc
注解。==
- 定义一个类,在类名上加上
-
原理:
-
在SpringMVC的自动配置类中,有如下代码:
1
2
3
4
5
6
.class) (EnableWebMvcConfiguration
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
}这个内部类在声明处做了如下几件事:
- @Configuration:声明该类是一个配置类
- @Import(EnableWebMvcConfiguration.class) 加载了EnableWebMvcConfiguration到容器
- @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) 为WebMvcProperties、ResourceProperties绑定了相应的配置文件。
-
既然加载了EnableWebMvcConfiguration这个类,那我们看看这个类的声明。如下:
1
2
3
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
}该类也是个配置类。我们看看他的父类做了什么。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
//自动注入所有的WebMvcConfigurer
false) (required =
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}可以看到他是会自动注入所有WebMvcConfigurer实例。而WebMvcConfigurerAdapter的声明是这样的。
1
2
3public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {
}而我们在扩展的时候,继承了
WebMvcConfigurerAdapter
,所以我们这个扩展类也是WebMvcConfigurer的子类,所以就会自动注入进去。实现和自动配置相互合作的效果。那么,他是怎么调用我们的呢?在DelegatingWebMvcConfiguration这个类中有这样的方法:
1
2
3
4
5
6
7
8
9
10
11
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}
//this.configurers.addViewControllers(registry)调的就是下面这个方法。
public void addViewControllers(ViewControllerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
delegate.addViewControllers(registry);
}
}可以看见,他是遍历所有的WebMvcConfigurer。
-
五、如何覆盖SpringMVC的自动配置
-
官方文档原文:
If you want to take complete control of Spring MVC, you can add your own
@Configuration
annotated with@EnableWebMvc
.-
翻译出来大概意思为:如果你需要完全控制SpringMVC,你需要做如下几点:
-
写一个类,上面加上
@Configuration
,并在上面加上@EnableWebMvc
。 -
原理:
-
在SpringMVC自动配置类的上的声明如下:
1
2
3
4
5
6
7
8
9
10
(type = Type.SERVLET)
.class, DispatcherServlet.class, WebMvcConfigurer.class }) ({ Servlet
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //不存在WebMvcConfigurationSupport这个类以下配置才生效
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}-
可以在上面看到,只有在容器里没有
WebMvcConfigurationSupport
这个类的实例,自动配置才会生效。 -
那么,现在我们看看
@EnableWebMvc
- @EnableWebMvc的声明
1
2
3
4
5
6(RetentionPolicy.RUNTIME)
(ElementType.TYPE)
.class) //往容器中添加DelegatingWebMvcConfiguration的实例 (DelegatingWebMvcConfiguration
public @interface EnableWebMvc {
} -
DelegatingWebMvcConfiguration
声明如下1
2
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}
-
1 |
|
六、结论
- 如何定制我们需要的SpringMVC组件?
- 只需要把我们需要的组件加入到容器中即可
- 如何扩展SpringMVC的功能?
- 编写一个配置类,即加上
@Configuration
- 该类继承
WebMvcConfigurerAdapter
- 不能再类名上加上
@EnableWebMvc
注解。
- 编写一个配置类,即加上
- 如何覆盖SpringMVC的自动配置
- 编写一个配置类,即加上
@Configuration
- 加上
@EnableWebMvc
注解
- 编写一个配置类,即加上