在繼續講解基於註解(@Value)的屬性配置之前,我們先介紹Spring的Environment,否則@Value的屬性來源會講不清楚。這與官方文檔的順序有所不同。本章內容對應官方文檔地址。
Environment是對Spring運行的外部環境抽象,Environment主要管理兩個概念:profiles和properties。
Profile
要準確給Pofile下一個定義還挺難的,官方文檔認爲Pofile可以理解爲若干bean集合的邏輯組名稱,而我認爲可理解爲Spring容器的運行時標籤。我們不妨先了解了Profile如何使用,然後自就在頭腦中建立起對它的認知。
激活Profile
Spring可以同時激活一個或多個Profile,他們記錄在Environment裏,激活一個Profile最直接的是調用Environment接口:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
另外就是通過設定spring.profiles.active屬性,如果通過命令行參數的話,類似這樣:
-Dspring.profiles.active="profile1,profile2"
默認Profile
如果沒有激活任何Proifle,那麼名叫“default”的profile會被激活。可以通過ctx.getEnvironment().setDefaultProfiles方法或者java系統屬性spring.profiles.default
來修改。
Profile機制
Profile給Spring提供了一種機制,在不同的環境裏註冊不同的Bean,比如:
- 在開發環境使用In-memory數據庫,在QA以及生產環境從JNDI查找數據源;
- 在性能測試環境在註冊那些性能檢測的bean;
- 企業軟件對不同的客戶部署定製化的bean。
滿足上面第一個需求的代碼類似:
@Configuration
@Profile("development")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
@Profile可以放在@Configuration類上面,也可以放在@Bean方法上面。一旦附加了@Profile,那麼只有指定的Profile被激活時,該@Configuration或@Bean纔有效。
邏輯操作符
@Profile註解支持邏輯操作:
@Profile(!profile)
, 邏輯非,代表當profile沒有激活;@Profile(profileA&profileB)
,邏輯與,代表當profileA和profileB都激活;@Profile(profileA|profileB)
,邏輯或,代表當profileA或profileB被激活;@Profile({profileA,profileB})
,逗號分隔的多個profile,相當於邏輯或
和java的邏輯操作符類似,可以組合這些操作符,通過括號來界定優先級。
Properties
Property是具名的值,也即name&value名值對。Spring容器定義了一個全局統一的Property查詢接口:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
String pvalue = env.getProperty("my-property");
雖然用統一的方式查詢,但屬性有多種來源,實際上,Environment抽象了一個可配置的、包含多種屬性源層級結構。屬性源對應的java定義是PropertySource。
PropertySource
Spring包含多種Environment實現,分別預設了不同的PropertySource,這些PropertySource是有序的,排在前面的有更高的優先級。比如StandardEnvironment,配置了兩個源:JVM系統屬性和系統環境變量,它的源碼一看就明白:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
而StandardServletEnvironment,則包含以下屬性源(按順序):
- ServletConfig參數
- ServletContext配置參數
- JNDI環境變量
- JVM系統屬性
- JVM環境變量
添加PropertySource
我們可以添加自己的PropertySource到Environment,下面的代碼將MyPropertySource添加爲Environment的第一個PropertySource,使其具備最高的優先級:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
在配置類上通過註解@PropertySource可以添加讀取屬性文件的PropertySource:
@Configuration
@PropertySource(value = {"classpath:propertySource.properties"})
public class AppConfig {
}
總結
Sping的環境被抽象爲Environment,Environment主要管理兩個概念,Profile和Properties。Profile是Spring的容器的標籤機制,通過激活不同的Profile,我們可以基於同一套代碼在運行時激活不同的bean。Properties是Spring容器範圍內共享的名值對,它來自多個PropertySource,我們可以添加自定義的PropertySource。