SpringBoot配置与自动配置原理
自动配置原理
SpringBoot启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration //!!!
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
@EnableAutoConfiguration
的作用:
- 利用
EnableAutoConfigurationImportSelector
给容器中导入一些组件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) //!!!
public @interface EnableAutoConfiguration
- 可以查看
AutoConfigurationImportSelector.selectImports()
方法的内容;
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);//!!!
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
getAutoConfigurationEntry()
方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);//!!!
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
getCandidateConfigurations()
方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
从META-INF下面找spring.factories
文件,将里面配置的所有EnableAutoConfiguration的值加入到了容器中

每一个这样的xxxAutoConfiguration
类都是容器中的一个组件,都加入到容器中;用他们来做自动配置。
例如:自动配置类DispatcherServletAutoConfiguration
(节选内部类DispatcherServletConfiguration)
//配置文件
@Configuration(proxyBeanMethods = false)
//配置条件满足类DefaultDispatcherServletCondition的验证
@Conditional(DefaultDispatcherServletCondition.class)
//如果存在ServletRegistration类则进行配置
@ConditionalOnClass(ServletRegistration.class)
//如果存在对应的属性配置(WebMvcProperties对应的是spring.mvc.*)则启用配置
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
//注意
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
@Bean
//如果存在类定义则配置
@ConditionalOnBean(MultipartResolver.class)
//判断如果不存在bean名称为DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME,则配置 bean
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
通过上面的代码,可以看到这个自动配置类使用了许多@Conditional**
注解来条件性地加载配置(比如说是否存在指定的类),配置的默认值从一个**Properties
类中取出,如果用户配置了自定义属性,会覆盖掉**Properties
中的默认值。
到这里,应该明白为什么几乎在没有任何配置下就能用Spring Boot启动 Spring MVC项目。但是有时候,我们需要对这些默认的环境进行修改以适应个性化的要求,这些在 Spring Boot中也是非常简单的,正如@EnableConfigurationProperties
注解那样,它允许读入配置文件的内容来自定义自动初始化所需的内容。
Spring Boot配置文件
Spring Boot的所有的AutoConfiguration属性项可以在 https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties 看到。这一节我们主要介绍自定义配置。
yaml语法
配置文件推荐使用yaml语法来写,通过缩进来表达数据关系。
字符串
字符串可以不加引号,如果加引号:
""
:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思name: "zhangsan \n lisi"
:\n
会解析为回车
''
:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据name: 'zhangsan \n lisi'
:\n
不会解析为回车
对象、Map
friends:
lastName: zhangsan
age: 20
行内写法:
friends: {lastName: zhangsan,age: 18}
数组(List、Set)
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
自定义属性配置
可以在类上加上@ConfigurationProperties(prefix="")
注解,把本类中所有属性和配置文件中相关的配置进行绑定。
只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能。
例:
JavaBean
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
//getter and setter...
}
配置:
person:
lastName: hello
age: 18
boss: false
birth: 2000/01/01
maps: {k1: v1,k2: 12}
lists:
- lisi
- zhaoliu
dog:
name: 小狗
age: 7
它默认从全局配置中获取值,可以使用@PropertySource(value={})
来指定配置文件。
还有一种方法是使用@Value
,它必须配合SpEL来指定对应配置的名称,不支持松散语法绑定(即大小写等不匹配)。
配置文件占位符
随机数
${random.value}
${random.int}
${random.long}
${random.int(10)}
${random.int[1024,65536]}
占位符获取之前配置的值,如果没有可以是用:指定默认值
person.last-name=张三{random.uuid}
person.age={random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=${person.hello:hello}_dog
person.dog.age=15
配置文件加载位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
file:./config/
file:./
classpath:/config/
classpath:/
优先级由高到底,高优先级的配置会覆盖低优先级的配置
SpringBoot会从这四个位置全部加载主配置文件,互补配置
我们还可以通过spring.config.location
来改变默认的配置文件位置。
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties
SpringBoot支持的所有的外部配置文件加载方法见:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config
@ImportResource注解
@ImportResource
可以帮助导入传统xml类型的配置,例如springcontext配置,直接将它写在启动类上就行了。
//导入Spring的配置文件让其生效
@ImportResource(locations = {"classpath:beans.xml"})
@SpringBootApplication
public class BootHelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(BootHelloworldApplication.class, args);
}
}
多环境化配置Profile
properties
在真实的应用中,常常会有多个环境(如:开发,测试,生产等),不同的环境数据库连接都不一样,这个时候就需要用到spring.profile.active的强大功能了,它的格式为 application-{profile}.properties
,这里的 application 为前缀不能改,{profile}
是我们自己定义的。
application.properties就是默认的配置文件。
在 application.properties
配置文件中写入 spring.profiles.active=dev
,就可以指定使用profile为dev的配置文件。
也可以使用虚拟机参数-Dspring.profiles.active=dev
指定
yaml文档块
# document 1
server:
port: 8081
spring:
profiles:
active: prod
---
# document 2
server:
port: 8083
spring:
profiles: dev
---
# document 3
server:
port: 8084
spring:
profiles: prod #指定属于哪个环境
文档块之间使用---
分隔,第一个文档块为默认使用的配置。
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/springboot%e8%87%aa%e5%8a%a8%e9%85%8d%e7%bd%ae%e5%8e%9f%e7%90%86%e4%b8%8e%e8%87%aa%e5%ae%9a%e4%b9%89%e9%85%8d%e7%bd%ae%e4%bb%8b%e7%bb%8d/