在咱們開發開發測試部署當中,有不一樣的環境,好比有:開發環境、測試環境、上產環境,不一樣的環境有不一樣的組件,這聽着怎麼那麼像springboot中的多環境配置呢?今天,我們來看看在spring中是如何實現的。java
爲了多環境開發,Spring爲咱們提供的能夠根據當前環境,動態的激活和切換一系列組件的功能,好比數據源組件的配置,不一樣開發環境鏈接的數據源可能會不一樣,就可使用@Profile註解進行配置,根據環境動態切換數據源組件。mysql
@Profile:指定組件在哪一個環境下才能被註冊到容器中,不指定則任何環境都能註冊這個組件,加了環境標識的bean,只有這個環境被激活的時候才能註冊到容器spring
就以數據源配置爲例,不一樣環境數據源的配置每每不一樣,如何使用@Profile註解在不一樣環境下進行數據源的註冊呢,經過實例來進行分析。sql
【1】@Profile環境搭建springboot
// 啓動測試類
@Test
public void TestMain() {
// 建立IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
// applicationContext.close();
}
// 配置類
@Configuration
public class AppConfig {
// 測試環境
// @Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
// 開發環境
// @Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
// 生產環境
// @Profile("pro")
@Bean("proDataSource")
public DataSource dataSourcePro() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
}
複製代碼
能夠看到,在配置類中,有不一樣的數據源配置,分別是測試環境、開發環境、生產環境,咱們能夠經過@Profile註解來指定注入哪一種環境,當不指定則任何環境都能註冊這個組件,也就是上面代碼,運行測試類,輸出結果以下,三個數據源組價都進行了注入:markdown
將數據源使用@Profile註解進行標識,也就是將上面代碼三個@Profile註解打開,此時由於沒有激活註冊環境,因此這個三個數據源都不能被注入。下面來進行激活。app
【2】激活註冊環境oop
@Profile("default")
-Dspring.profiles.active=dev
@Profile("default")
,標識默認當前環境// 測試環境
@Profile("default")
// @Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("806188");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/vhr");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
複製代碼
-Dspring.profiles.active=dev
,經過該配置來標註是何種環境運行啓動類,能夠看到devDataSource
被注入:測試
@Test
public void TestMain() {
// 建立IOC容器
// AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 設置須要激活的環境
applicationContext.getEnvironment().setActiveProfiles("pro");
// 註冊主配置類
applicationContext.register(AppConfig.class);
// 啓動刷新容器
applicationContext.refresh();
String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}
}
複製代碼
修改後,手動設置須要激活的環境,運行啓動類,輸出結果以下:spa
@Profile("test")
@Configuration
public class AppConfig {...}
複製代碼
運行啓動類,此時啓動類是手動代碼配置了pro環境,由於配置類上是@Profile("test")
,只有在test環境下該配置類纔會生效,因此沒有輸出:
查看@Profile註解源碼,咱們能夠看到,@Profile其實是一個@Conditional註解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}
複製代碼
@Conditional
註解在前面的文章講解過,能夠進行參考:@Conditional註解 -【Spring底層原理】,這裏簡單分析一下
@Conditinal
是一個條件註解,參數是一個class,這個class都要實現Condition
接口,重寫matches()
方法。例如,上面示例代碼中的ProfileCondition.class
class ProfileCondition implements Condition {
ProfileCondition() {
}
// Spring從ConditionContext中拿到激活的Profile和註解上的字符串進行比對,以決定是否實例化這個bean
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
do {
if (!var4.hasNext()) {
return false;
}
value = var4.next();
} while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true;
} else {
return true;
}
}
}
複製代碼
mathces()
方法的返回值是一個布爾值,返回true時,spring就會建立這個bean,返回false時就不會建立mathes()
方法上有兩個參數,分別是ConditionContext
和AnnotatedTypeMetadata
,這連個參數中包含了大量的信息,ConditionContext
中有Environment
,ClassLoader
等信息,AnnotatedTypeMetadata
能夠得到註解的信息- Spring從
ConditionContext
中拿到激活的Profile和註解上的字符串進行比對,以決定是否實例化這個bean
@Profile註解是用來指定組件在哪一個環境下才能被註冊到容器中的,只有這個環境被激活的時候才能註冊到容器,激活總結爲如下幾種方式:
@Profile("default")
-Dspring.profiles.active=dev