Spring中的Profile功能可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring IoC容器中。
举个更具体的例子,我们以前所定义的Bean,当Spring容器一启动的时候,就会一股脑的全部加载我们自定义配置的信息和完成对Bean的创建;而使用了Profile之后,它会将Bean的定义进行更细粒度的划分,将这些定义的Bean划分为几个不同的组,当Spring容器加载配置信息的时候,首先查找激活的Profile,然后只会去加载被激活的组中所定义的Bean信息,而不被激活的Profile中所定义的Bean定义信息是不会被加载,也就不会创建这些Bean。
由于我们平时在开发中,通常会出现在开发的时候使用一个开发用的数据库,测试的时候使用一个测试的数据库,而实际部署的时候需要一个数据库。而使用了Profile之后,我们就可以分别定义3个配置文件,一个用于开发、一个用于测试、一个用于生产,其分别对应于3个Profile。当在实际运行的时候,只需给定一个参数来激活对应的Profile即可,那么容器就会只加载激活后的配置文件,这样就可以大大省去我们修改配置信息而带来的烦恼。
下面通过以数据源配置为例,在配置环境中使用@Profile注解来管理Bean的装配。
1.创建一个Maven项目,并且引入以下依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- c3p0依赖包 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
2.创建一个配置文件dbconfig.properties,其中添加访问数据库所需的基本信息。
db.user=root
db.password=123
db.driverClass=com.mysql.jdbc.Driver
3.创建一个配置类SpringConfigOfProfile,其中配置三个数据源,并用@Profile注解来进行标识。
@Configuration
public class SpringConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;// 用户名
@Value("${db.password}")
private String password;// 密码
private StringValueResolver stringValueResolver;
private String driverClass;// 数据库驱动
@Bean
Person Person() {// 没被@Profile注解标识则在任何环境都加载
return new Person();
}
@Profile("test") // 测试环境标识
@Bean("dataSourceOfTest")
public DataSource dataSourceOfTest() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
// 配置数据库连接池ComboPooledDataSource
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
@Profile("development") // 开发环境标识
@Bean("dataSourceOfDev")
public DataSource dataSourceOfDev() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/A");
return dataSource;
}
@Profile("production") // 生产环境标识
@Bean("dataSourceOfPro")
public DataSource dataSourceOfPro() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/production");
return dataSource;
}
/*
* 实现EmbeddedValueResolverAware接口,实现其setEmbeddedValueResolver()方法,
* 使用StringValueResolver解析配置文件
*/
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
this.stringValueResolver = resolver;
this.driverClass = stringValueResolver.resolveStringValue("${db.driverClass}");
}
}
4.创建一个测试类,添加一个测试方法test(),在方法中创建一个 AnnotationConfigApplicationContext对象并设置需要激活的环境。
public class ProfileTest {
@Test
public void test() {
// 获取Spring的IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// // 设置要激活的环境
applicationContext.getEnvironment().setActiveProfiles("development");
// // 注册配置类
applicationContext.register(SpringConfigOfProfile.class);
applicationContext.refresh();
// 从容器中获取bean
String[] names = applicationContext.getBeanDefinitionNames();
for (String i : names) {
System.out.println(i);
}
}
5.运行结果:
可以看到加了@Profile注解标识的Bean,因为只有development环境被激活了,所以只注册了dataSourceOfDev到容器中。而没有加@Profile注解标识的Bean,在任何环境下都会注册到Spring IoC容器中。
当@Profile注解写在配置类上时,只有在指定的环境的时候,整个配置类里面的所有配置才能生效。
否则配置类中包括标识了@Bean的方法和@import注解定义的内容等配置信息都不会生效。
6.另一种激活环境的方式是直接配置运行环境,在运行配置中的VM参数添加-Dspring.profiles.active=test
来激活test环境。ApplicationContext中配置激活的环境优先级大于这种方式。
添加测试方法test2:
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(
SpringConfigOfProfile.class);
// 从容器中获取bean
String[] names = applicationContext.getBeanDefinitionNames();
for (String i : names) {
System.out.println(i);
}
}
运行这个测试方法,查看结果:
另外,在XML配置文件文件中可以这样配置环境:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>test</param-value>
</context-param>
注意:我们可以同时激活多个的Profile,如spring.profiles.active 可以接受以逗号分隔的配置文件名列表:
-Dspring.profiles.active="profile1,profile2"
或者向 setActiveProfiles ()方法提供多个配置文件名,该方法接受多个String类型参数:
applicationContext.getEnvironment().setActiveProfiles("profile1", "profile2");
Spring通过两个不同属性来决定哪些profile可以被激活,一个属性是spring.profiles.active和spring.profiles.default。这两个常量值在Spring的AbstractEnvironment中有定义。如果当spring.profiles.active属性被设置时,那么Spring会优先使用该属性对应值来激活Profile。当spring.profiles.active没有被设置时,那么Spring会根据spring.profiles.default属性的对应值来激活Profile。
可以使用 applicationContext.getEnvironment().setDefaultProfiles(profiles)
或使用 spring.profiles.default
属性以声明方式更改默认Profile的名称。
如果上面的两个属性都没有被设置,那么就不会有任何Profile被激活,只有定义在Profile之外的Bean才会被创建。我们发现这两个属性值其实是Spring容器中定义的属性,而我们在实际的开发中很少会直接操作Spring容器本身,所以如果要设置这两个属性,其实是需要定义在特殊的位置,让Spring容器自动去这些位置读取然后自动设置,这些位置主要为如下定义的地方:
参考:详解Spring中的Profile