Springboot的自动配置
了解配置
SpringBoot官方文档中有大量的配置,我们无法全部记住
自动配置分析
我们以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
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// 表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
// 启动指定类的ConfigurationProperties功能;
// 进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
// 并把HttpProperties加入到ioc容器中
// Spring底层@Conditional注解
// 根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
// 这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
// 判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
// 判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
// 如果不存在,判断也是成立的
// 即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration {
// 他已经和SpringBoot的配置文件映射了
private final Encoding properties;
// 只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
// 给容器中添加一个组件,这个组件的某些值需要从properties中获取
//判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
//。。。。。。。
}一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!
一但这个配置类生效;这个配置类就会给容器中添加各种组件;
这些组件的属性是从对应的properties类中获取的
@EnableConfigurationProperties({XXXXProperties.class})
这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
配置文件能配置什么就可以参照某个功能对应的这个属性类,这就是自动装配的原理!
总结
SpringBoot启动会加载大量的自动配置类
我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfigurartion
:自动配置类, 给容器中添加组件。xxxxProperties
:封装配置文件中相关属性。
@Conditional注解
- 了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;
@Conditional派生注解
作用:必须是
@Conditional
指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;1
2
3
4
5
6
7
8
9
10
11
12@ConditionalOnClass : classpath中存在该类时起效
@ConditionalOnMissingClass : classpath中不存在该类时起效
@ConditionalOnBean : DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression : SpEL表达式结果为true时
@ConditionalOnProperty : 参数设置或者值一致时起效
@ConditionalOnResource : 指定的文件存在时起效
@ConditionalOnJndi : 指定的JNDI存在时起效
@ConditionalOnJava : 指定的Java版本存在时起效
@ConditionalOnWebApplication : Web应用环境下起效
@ConditionalOnNotWebApplication : 非Web应用环境下起效@Conditional注解主要用在以下位置:
- 可以放在注标识有@Component(包含@Configuration)的类上
- 作为一个meta-annotation,组成自定义注解
- 方法级别可以放在标识由@Bean的方法上
- 如果一个@Configuration的类标记了@Conditional,所有标识了@Bean的方法和@Import注解导入的相关类将遵从这些条件。
1
2
3
4
5
6//@Conditional定义
public Conditional{
Class<? extends Condition>[] value();
}
示例
实现Condtin接口
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
41package top.fulsun.helloworld.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @className: LinuxCondition
* @Description: 实现了Condtin接口,重载的方法返回一个基于操作系统类型的布尔值。
* @version: v1.8.0
* @author: Fulsun
* @date: 2020/3/27 22:14
*/
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Linux");
}
}
//===================================
package top.fulsun.helloworld.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @className: WindowsCondition
* @Description: 实现了Condtin接口,重载的方法返回一个基于操作系统类型的布尔值。
* @version: v1.8.0
* @author: Fulsun
* @date: 2020/3/27 22:15
*/
public class WindowsCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").contains("Windows");
}
}定义两个bean
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
27package top.fulsun.helloworld.condition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* @className: MyConfiguration
* @Description: 定义两个bean, 一个符合条件另外一个不符合条件:
* @version: v1.8.0
* @author: Fulsun
* @date: 2020/3/27 22:18
*/
public class MyConfiguration {
public void windowsEmailerService() {
System.out.println("send windows email");
}
public void linuxEmailerService() {
System.out.println("send linux email");
}
}测试:启动主启动类,观察控制台的输出即可
查看生效配置类
那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。我们怎么知道哪些自动配置类生效?
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
1
2#开启springboot的调试类
debug=truePositive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)
MVC 自动配置原理
SpringBoot对我们的SpringMVC还做了哪些配置,我们如何扩展,如何定制?
官网阅读地址:官方文档
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
48Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自动配置在Spring默认设置的基础上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars
// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
Support for HttpMessageConverters (covered later in this document).
// 定义错误代码生成规则的
Automatic registration of MessageCodesResolver (covered later in this document).
// 首页定制
Static index.html support.
// 图标定制
Custom Favicon support (covered later in this document).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),
则可以添加自己的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。
如果希望提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandler,ExceptionResolver的自定义实例,
则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration
(interceptors, formatters, view controllers, and other features), you can add your own
class of type WebMvcConfigurer but without . If you wish to provide
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.
// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can add your own annotated with .
spring-boot-starter-web
首先打开Maven的本地仓库,找到对应Spring Boot的文件夹,打开spring-boot-start-web文件夹
就可以看到一个名为 spring-boot-starter-web-2.0.7.RELEASE.pom 的文件。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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>2.0.7.RELEASE</version>
</parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.7.RELEASE</version>
<name>Spring Boot Web Starter</name>
<description>Starter for building web, including RESTful, applications using Spring
MVC. Uses Tomcat as the default embedded container</description>
<url>https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-starters/spring-boot-starter-web</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>https://spring.io</url>
</organization>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<developers>
<developer>
<name>Pivotal</name>
<email>info@pivotal.io</email>
<organization>Pivotal Software, Inc.</organization>
<organizationUrl>http://www.spring.io</organizationUrl>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/spring-projects/spring-boot.git/spring-boot-starters/spring-boot-starter-web</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-boot.git/spring-boot-starters/spring-boot-starter-web</developerConnection>
<url>http://github.com/spring-projects/spring-boot/spring-boot-starters/spring-boot-starter-web</url>
</scm>
<issueManagement>
<system>Github</system>
<url>https://github.com/spring-projects/spring-boot/issues</url>
</issueManagement>
<dependencies>
<!-- Spring Boot 的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- json 的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- tomcat 的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- hibernate 的依赖-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
<scope>compile</scope>
</dependency>
<!-- Spring Web MVC 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.11.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
- 通过代码中的中文注释可以看出,当加入spring-boot-starter-web后,它会通过Maven将对应的资源加载到我们的工程中,这样便能够形成依赖。但是这样还不足以运行 Spring MVC项目,要运行它还需要对Spring MVC进行配置,让它能够生产Spring MVC所需的对象,才能启用 Spring MVC,所以还需要进一步探讨。
- 为了探讨Spring MVC在Spring Boot自动配置的问题,首先在本地下载的Maven仓库的目录 spring-boot-autoconfigure 中找到 spring-boot-autoconfigure-2.0.7.RELEASE-sources.jar 的包。它是一个 源码包,把它解压缩出来,打开它目录下的子目录org\ppringframework\boot\autoconfigure\web\servlet后,我们就可以看到许多配置类,这里可以看到存在很多的类,其中DispatcherServletAutoConfiguration就是一个对 DispatcherServlet 进行自动配置的类。
DispatcherServletAutoConfiguration
找到DispatcherServletAutoConfiguration
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//配置文件
//配置条件满足类 DefaultDispatcherServletCondition 的验证
//如果存在ServletRegistration类则进行配置
//如果存在对应的属性配置(Spring MVC 的是spring.mvc.*)则启用配置
protected static class DispatcherServletConfiguration {
private final WebMvcProperties webMvcProperties;
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
this.webMvcProperties = webMvcProperties;
}
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(
this.webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(
this.webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(
this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
return dispatcherServlet;
}
//如果存在类定义则配置
// 判断如果不存在 bean 名称为 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME,则配置 Bean
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}注意上述代码中
@EnableConfigurationProperties(WebMvcProperties.class)
的注释,这些中文注释是我加入的,为的是更好地说明 Spring Boot 的自动配置功能。通过上面的代码, 可以看到 Spring Boot 内部己经自动 为我们做了很多关于 DispatcherServlet 的配置,其中的 @EnableConfigurationProperties 还能够在读取配置内容的情况下自动生成 Spring MVC 所需的类,到这里应该明白为什么几乎在没有任何配置下就能用 Spring Boot 启 动 Spring MVC 项目
这些都是 Spring Boot 通过Maven 依赖找到对应的jar包和嵌入的服务器,然后使用默认自动配置类来创建默认的开发环境。但是有时候,我们需要对这些默认的环境进行修改以适应个性化的要求,这些在Spring Boot 中也是非常简单的,正如@EnableConfigurationProperties 注解那样,它允许读入配置文件的内容来自定义自动初始化所需内。
视图解析器
ContentNegotiatingViewResolver :视图解析器
SpringBoot自动配置了ViewResolver,就是我们之前学习的SpringMVC的视图解析器;
- 即根据方法的返回值取得视图对象(View),然后由视图对象决定如何渲染(转发,重定向)。
1
2
3
4
5// 实现了 ViewResolver 接口的类,我们称为视图解析器
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
implements ViewResolver, Ordered, InitializingBean {
//....
}1
2
3
4
5
6public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}查看这里的源码:我们找到 WebMvcAutoConfiguration , 然后搜索ContentNegotiatingViewResolver。找到如下方法!
1
2
3
4
5
6
7
8
9
10
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));
// ContentNegotiatingViewResolver使用所有其他视图解析器来定位视图,因此它应该具有较高的优先级
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}我们可以点进ContentNegotiatingViewResolver类看看!找到对应的解析视图的代码;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 注解说明:@Nullable 即参数可为null
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
// 获取候选的视图对象
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
// 选择一个最适合的视图对象,然后把这个对象返回
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
// .....
}我们继续点进去看,他是怎么获得候选的视图的呢?
getCandidateViews中看到他是把所有的视图解析器拿来,进行for循环,挨个解析!
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
32private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
throws Exception {
List<View> candidateViews = new ArrayList<>();
if (this.viewResolvers != null) {
Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");
//遍历所有的视图解析器
for (ViewResolver viewResolver : this.viewResolvers) {
//封装到对象
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
//添加到候选的视图
candidateViews.add(view);
}
for (MediaType requestedMediaType : requestedMediaTypes) {
List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
for (String extension : extensions) {
String viewNameWithExtension = viewName + '.' + extension;
view = viewResolver.resolveViewName(viewNameWithExtension, locale);
if (view != null) {
candidateViews.add(view);
}
}
}
}
}
if (!CollectionUtils.isEmpty(this.defaultViews)) {
candidateViews.addAll(this.defaultViews);
}
//返回候选的视图
return candidateViews;
}所以得出结论:ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的 ,再研究下他的组合逻辑,看到有个属性viewResolvers,看看它是在哪里进行赋值的!
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
protected void initServletContext(ServletContext servletContext) {
// 这里它是从beanFactory工具中获取容器中的所有视图解析器
// ViewRescolver.class 把所有的视图解析器来组合的
Collection<ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList<>(matchingBeans.size());
for (ViewResolver viewResolver : matchingBeans) {
if (this != viewResolver) {
this.viewResolvers.add(viewResolver);
}
}
}
else {
for (int i = 0; i < this.viewResolvers.size(); i++) {
ViewResolver vr = this.viewResolvers.get(i);
if (matchingBeans.contains(vr)) {
continue;
}
String name = vr.getClass().getName() + i;
obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
}
}
AnnotationAwareOrderComparator.sort(this.viewResolvers);
this.cnmFactoryBean.setServletContext(servletContext);
}
定义视图解析器
既然它是在容器中去找视图解析器,我们是否可以猜想,我们就可以去实现一个视图解析器了呢?
我们可以自己给容器中去添加一个视图解析器;这个类就会帮我们自动的将它组合进来;
我们在我们的主程序中去写一个视图解析器来试试;
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
32package top.fulsun.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import java.util.Locale;
/**
* @className: MyViewResolver
* @Description: 实现WebMvcConfigurer接口
* @version: v1.8.0
* @author: Fulsun
* @date: 2020/4/5 1:58 下午
*/
//配置类
public class MyWebMvcConfig implements WebMvcConfigurer {
//放到bean中
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}怎么看我们自己写的视图解析器有没有起作用呢?
我们给 DispatcherServlet 中的 doDispatch方法 加个断点进行调试一下,因为所有的请求都会走到这个方法中
我们启动我们的项目,然后随便访问一个页面,看一下Debug信息;
找到this,找到视图解析器,我们看到我们自己定义的就在这里了;
所以说,我们如果想要使用自己定制化的东西,我们只需要给容器中添加这个组件就好了!剩下的事情SpringBoot就会帮我们做了!
转换器和格式化器
WebMvcAutoConfiguration中找到格式化转换器:
1
2
3
4
5
6
7
8
9
public FormattingConversionService mvcConversionService() {
// 拿到配置文件中的格式化规则
WebConversionService conversionService =
new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}点击去:
1
2
3
4
5
6
7
8public String getDateFormat() {
return this.dateFormat;
}
/**
* Date format to use. For instance, `dd/MM/yyyy`. 默认的
*/
private String dateFormat;我们可以根据自动配置类prefix,在yaml中进行自动配置dateFormat
如果配置了自己的格式化方式,就会注册到Bean中生效,我们可以在配置文件中配置日期格式化的规则:
1
2
3spring:
mvc:
date-format: dd-MM-yyyy
修改SpringBoot的默认配置
- 这么多的自动配置,原理都是一样的.
- SpringBoot的底层,大量用到了这些设计细节思想,所以,没事需要多阅读源码!得出结论;
- SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,如果没有就用自动配置的;
- 如果有些组件可以存在多个,比如我们的视图解析器,就将用户配置的和自己默认的组合起来!
扩展使用SpringMVC
编写一个@Configuration注解类,并且类型要为WebMvcConfigurer,还不能标注@EnableWebMvc注解;我们去自己写一个;我们新建一个包叫config,写一个类MyMvcConfig;
1
2
3
4
5
6
7
8
9
10
11//应为类型要求为WebMvcConfigurer,所以我们实现其接口
//可以使用自定义类扩展MVC的功能
public class MyMvcConfig implements WebMvcConfigurer {
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/2 , 就会跳转到test页面;
registry.addViewController("/t2").setViewName("test");
}
}我们去浏览器访问一下:
确实也跳转过来了!所以说,我们要扩展SpringMVC,官方就推荐我们这么去使用,既保SpringBoot留所有的自动配置,也能用我们扩展的配置!
扩展MVC的原理
WebMvcAutoConfiguration
是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter
这个类上有一个注解,在做其他自动配置时会导入:
@Import(EnableWebMvcConfiguration.class)
我们点进
EnableWebMvcConfiguration
这个类看一下,它继承了一个类:DelegatingWebMvcConfiguration
这个父类中有这样一段代码:
1
2
3
4
5
6
7
8
9
10public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
// 从容器中获取所有的webmvcConfigurer
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
}我们可以在这个类中去寻找addViewControllers当做参考,发现它调用了一个
1
2
3
4
protected void addViewControllers(ViewControllerRegistry registry) {
this.configurers.addViewControllers(registry);
}我们点进去看一下
1
2
3
4
5
6
7
public void addViewControllers(ViewControllerRegistry registry) {
for (WebMvcConfigurer delegate : this.delegates) {
// 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
delegate.addViewControllers(registry);
}
}
- 所以得出结论:所有的
WebMvcConfiguration
都会被作用,不止Spring自己的配置类,我们自己的配置类当然也会被调用;
全面接管SpringMVC
官方文档:If you want to take complete control of Spring MVCyou can add your own @Configuration annotated with @EnableWebMvc.
全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!
只需在我们的配置类中要加一个
@EnableWebMvc
。我们看下如果我们全面接管了SpringMVC了,我们之前SpringBoot给我们配置的静态资源映射一定会无效,我们可以去测试一下;
不加注解之前,访问首页:
给配置类加上注解:
@EnableWebMvc
我们发现所有的SpringMVC自动配置都失效了!回归到了最初的样子;我们开发中,不推荐使用全面接管SpringMVC
失效原因
- 为什么加了一个注解,自动配置就失效了!我们看下源码:
这里发现它是导入了一个类,我们可以继续进去
1
2
3
public EnableWebMvc {
}它继承了一个父类
WebMvcConfigurationSupport
1
2
3public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
// ......
}我们来回顾一下Webmvc自动配置类
1
2
3
4
5
6
7
8
9
10
11
// 这个注解的意思就是:容器中没有这个组件的时候,这个自动配置类才生效
public class WebMvcAutoConfiguration {
}总结一句话:
@EnableWebMvc
将WebMvcConfigurationSupport
组件导入进来了;所以自动配置类就不生效了.
掌握并理解原理,即可以不变应万变!就可以不用背配置文件啦。