一、Spring中的Profile
Spring中的Profile功能可以理解爲我們在Spring容器中所定義的Bean的邏輯組名稱,只有當這些Profile被激活的時候,纔會將Profile中所對應的Bean註冊到Spring IoC容器中。
舉個更具體的例子,我們以前所定義的Bean,當Spring容器一啓動的時候,就會一股腦的全部加載我們自定義配置的信息和完成對Bean的創建;而使用了Profile之後,它會將Bean的定義進行更細粒度的劃分,將這些定義的Bean劃分爲幾個不同的組,當Spring容器加載配置信息的時候,首先查找激活的Profile,然後只會去加載被激活的組中所定義的Bean信息,而不被激活的Profile中所定義的Bean定義信息是不會被加載,也就不會創建這些Bean。
二、爲什麼要使用Profile
由於我們平時在開發中,通常會出現在開發的時候使用一個開發用的數據庫,測試的時候使用一個測試的數據庫,而實際部署的時候需要一個數據庫。而使用了Profile之後,我們就可以分別定義3個配置文件,一個用於開發、一個用於測試、一個用於生產,其分別對應於3個Profile。當在實際運行的時候,只需給定一個參數來激活對應的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的方式
注意:我們可以同時激活多個的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容器自動去這些位置讀取然後自動設置,這些位置主要爲如下定義的地方:
- 作爲SpringMVC中的DispatcherServlet的初始化參數
- 作爲Web 應用上下文中的初始化參數
- 作爲JNDI的入口
- 作爲環境變量
- 作爲虛擬機的系統參數
- 使用@AtivceProfile來進行激活