1.概述
- springboot项目可通过配置文件进行一些参数配置,如数据库连接,redis连接等。在application.yml中配置这些参数后,参数就会自动生效,本文解析springboot如何实现自动化配置;
- 一般情况下,每个种类的配置文件都会有对应的java配置类与之对应。如数据库连接配置,与之对应的是DataSourceProperties类。ide可直接通过点击配置文件链接到对应的java配置类;
- 配置文件的加载可解析为两步。一是读取配置文件中的数据并绑定到java配置类,二是加载java配置类到spring容器中;
2.加载过程
2.1.读取配置文件
首先读取配置文件中的数据绑定到java配置类对应的属性中;
A.@Value注解
@Value单个注入配置文件的属性;
B.@ConfigurationProperties注解
@ConfigurationProperties批量注入配置文件的属性;
C.Environment.getProperty()方法
- Environment对象可以通过依赖注入/实现EnvironmentAware接口获取;
- Environment.getProperty()方法可以获取配置文件的属性,然后通过代码初始化到指定的变量;
2.2.加载配置bean
上一步把配置文件中配置的参数绑定到了java配置类,然后需要把java配置类加载到spring容器中,配置才会生效;
A.@Configuration注解
- spring加载组件的注解包括@Configuration @Controller @Service @Repository @Component;
- 加载组件注解功能基本都一样,根据字面意义标注不同的组件,提高代码可读性,加载配置一般使用@Configuration;
- 注意: 使用加载组件注解,需要spring扫描到配置类,然后才会加载到spring容器中。所以适用于应用系统中使用,不适用于第三方类库项目中使用(因为应用系统中一般不会配置spring扫描第三方类库);
B.@EnableConfigurationProperties注解
- @EnableConfigurationProperties注解可配置需要加载的配置类。无论配置类在哪里,spring只要能引用到该配置类,就能加载;
- @EnableConfigurationProperties注解本身需要标注在spring能加载的类上面,才会生效;
- 注意:@EnableConfigurationProperties可以加载应用系统中的配置类,也可加载第三方类库项目中的配置类。但是加载第三方类库项目中的配置类,需要标注此注解的类能被spring加载到;
C.spring.factories配置文件
- 自动化配置原理。简单来说,就是@EnableAutoConfiguration会扫描项目所有依赖包的spring.factories文件,把key=EnableAutoConfiguration的类加载到spring容器中;
- 示例:在本项目创建resources/META-INF/spring.factories文件,并配置key=EnableAutoConfiguration的数据加载自定义的自动配置类
//**resources/META-INF/spring.factories配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.pings.spring.demo.cache.MyAutoConfiguration
- @EnableAutoConfiguration会自动加载该配置类。在本项目中配置好后,其它项目引用本项目时,无需额外的配置,仍然有效,多数第三方类库会采用此方式;
- 注意:自动化配置可以加载应用系统中的配置类,也可加载第三方类库项目中的配置类。但是应用系统中的配置类,采用前面两种方式更方便,一般应用于第三方类库项目中;
3.实例解析
我们以阿里的druid数据源为例,看看它如何实现自动化配置;
3.1.spring.factories
- 上一节讲到,第三方类库一般都是通过spring.factories配置文件加载配置bean,便于用户引入后,不用添加额外的配置;
- druid-spring-boot-starter包的META-INF目录下配置了spring.factories。DruidDataSourceAutoConfigure是的druid数据源自动配置的入口;
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
3.2.DruidDataSourceAutoConfigure
- DruidDataSourceAutoConfigure是的druid数据源自动配置的入口;
- @EnableConfigurationProperties注解加载了DruidStatProperties和DataSourceProperties两个配置类,获取用户配置的数据源参数。其中DataSourceProperties是spring提供的数据源配置,而DruidStatProperties是druid监控配置;
- @Import注解导入druid依赖的其它配置类;
- 注册DruidDataSourceWrapper数据源,并调用init初始化方法进行初始化设置。DruidDataSourceWrapper是DataSource的druid实现;
//**加载组件(此处不配置也可以加载该配置类)
@Configuration
//**引入了druid依赖才生效
@ConditionalOnClass(DruidDataSource.class)
//**在DataSourceAutoConfiguration自动化配置生效之前配置
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
//**加载DruidStatProperties和DataSourceProperties配置两个配置类
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
//**导入druid依赖的配置类
@Import({DruidSpringAopConfiguration.class,
DruidStatViewServletConfiguration.class,
DruidWebStatFilterConfiguration.class,
DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);
//**注册DruidDataSourceWrapper数据源,并调用init初始化方法进行初始化设置
@Bean(initMethod = "init")
@ConditionalOnMissingBean
public DataSource dataSource() {
LOGGER.info("Init DruidDataSource");
return new DruidDataSourceWrapper();
}
}
3.3.读取配置
- druid读取配置文件有使用了多种方式;
A.DruidStatProperties
DruidStatProperties使用@ConfigurationProperties注解读取监控相关配置;
@ConfigurationProperties("spring.datasource.druid")
public class DruidStatProperties {
...
}
B.DruidDataSourceWrapper
- 首先使用@ConfigurationProperties注解读取数据源相关配置;
@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean
//**从DruidDataSource继承的属性也能通过@ConfigurationProperties注解读取配置
...
}
- 然后使用代码从System获取取数据源相关配置覆盖@ConfigurationProperties注解读取的配置。比如设置启动参数-Ddruid.name=test,即可替换掉application.yml中配置的druid.name参数;
public class DruidDataSource {
...
public DruidDataSource(boolean fairLock){
super(fairLock);
//**获取System的配置
configFromPropety(System.getProperties());
}
public void configFromPropety(Properties properties) {
//**如果System配置了druid.name,则替换掉@ConfigurationProperties注解读取的配置
String property = properties.getProperty("druid.name");
if (property != null) {
this.setName(property);
}
}
...
}
4.补充说明
**-spring-boot-starter包的作用就是为了实现自动化配置;
- spring-boot-starter包引入其依赖的包;
- spring-boot-starter包有spring.factories配置,引入自动化配置的入口**AutoConfigure;
- spring-boot-starter包有**AutoConfigure,读取配置文件配置,并加载其依赖的配置类;