天天看點

Spring學習筆記9_環境抽象(Environment Abstraction)

文章參考來源: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>