文章參考來源:Spring Framework官方文檔
關于Environment(org.springframework.core.env.Environment) ,它是內建在容器中的一個抽象接口,它的使用展現在應用程式環境的兩個方面:概要檔案和屬性(profiles and properties)。
- 概要檔案是一個命名的、邏輯的bean定義組,隻有在給定的概要檔案是active活動的情況下才向容器注冊。無論是用XML定義的還是用注釋定義的bean,都會被配置設定給一個概要檔案。與概要檔案相關的Environment對象的作用是确定哪些概要檔案(如果有的話)目前是活動的,以及哪些概要檔案(如果有的話)在預設情況下應該是活動的。
- 屬性在幾乎所有的應用程式中都扮演着重要的角色,它可能起源于各種來源:屬性檔案、JVM系統屬性、系統環境變量、JNDI、servlet上下文參數、特别的Properties對象、Map對象等等。與屬性相關的Environment對象的作用是為使用者提供友善的服務接口,用于配置屬性源并從它們解析屬性。
概括來說,Environment 的作用就是:區分active環境,并配置解析對應屬性。
1. Profile
(1)@Profile的使用
@Profile注解,用來标注active環境。比如以下例子中,不同環境下使用的資料庫不同:
@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注解括号中的string,可以是profile name也可以是profile 表達式。profile 表達式允許一些複雜的表達式邏輯,比如“production & us-east”,其中:
- !表示“非”,not
- &表示“與”,and
- |表示“或”,or
以上表達式支援混合使用,比如production & us-east | eu-central或者production & (us-east | eu-central)
甚至可以支援自定義注解來标注環境,比如使用@Profile注解生成一個@Production注解,标注其環境名為:production
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
@Profile注解不僅可以用在配置類上,也可以使用在方法級别,比如:
@Configuration
public class AppConfig {
@Bean("dataSource")
@Profile("development")
public DataSource standaloneDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
@Bean("dataSource")
@Profile("production")
public DataSource jndiDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
profile不僅可以在java代碼中以注解方式起作用(@Profile),也可以在XML配置profile屬性:
<beans profile="development"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
profile屬性也可以在同一個檔案中展現:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!-- other bean definitions -->
<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
(2)激活Profile
激活方式:
- (1)利用ApplicationContext
- (2)配置檔案中設定屬性:spring.profiles.active
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
甚至可以激活多個profile:
預設的Profile配置長這樣:
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
如果沒有profile出于active狀态,那麼dataSource會被建立。哪怕隻有一個profile出于active,那麼預設的profile将不會起效。
如果想修改預設profile,可以選擇Environment的setDefaultProfiles()方法,或者設定spring.profiles.default屬性。
2. PropertySource
(1)關于PropertySource的簡單了解
PropertySource是對任何鍵值對源的一個簡單抽象。
Spring的StandardEnvironment配置了兩個PropertySource對象——一個表示JVM系統屬性集(system . getproperties()),另一個表示系統環境變量集(system .getenv())。
對于獨立應用程式而言,StandardEnvironment能夠提供預設屬性源。像StandardServletEnvironment,它使用了額外的預設屬性源,包括servlet配置和servlet上下文參數。它甚至可以選擇性啟用一個JndiPropertySource。
可以自定義添加自己的PropertySource,比如:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
(2)@PropertySource注解的使用
@PropertySource為将PropertySource添加到Spring環境提供了一種簡單友善的聲明性機制。
在下面的例子中,假設檔案app.properties中包含鍵值對屬性為:testbean.name=myTestBean,以下@Configuration配置類使用@PropertySource注解,将testbean.name引入并注入到testBean的name上:
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
關于語句中的占位符:
- 之前,占位符的解析隻能根據JVM系統屬性或環境變量。(也就是隻有在JVM系統屬性或環境變量中設定過的占位符值,才可以被解析到)
- 現在情況不再是這樣了。因為Environment抽象方法在整個容器中內建,是以很容易通過它來路由占位符的解析。
- 這意味着可以以任何方式配置解析過程。甚至可以更改搜尋系統屬性和環境變量的優先級,或者完全删除它們。還可以根據需要将自己的屬性源添加到該組合中。
也就是說,無論自定義屬性在哪裡定義,隻要它在Environment環境中可用,那麼聲明都是可解析有效的:
<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>