Spring基礎九:環境Environment

在繼續講解基於註解(@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,比如:

  1. 在開發環境使用In-memory數據庫,在QA以及生產環境從JNDI查找數據源;
  2. 在性能測試環境在註冊那些性能檢測的bean;
  3. 企業軟件對不同的客戶部署定製化的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,則包含以下屬性源(按順序):

  1. ServletConfig參數
  2. ServletContext配置參數
  3. JNDI環境變量
  4. JVM系統屬性
  5. 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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章