0x03.Environment

[TOC]

Environment是集成在容器中的抽象,它为应用程序环境的两个关键方面建模:概要文件(Profile)和属性(Properties)。

一个Profile是一个命名的、逻辑的bean定义组,只有在给定的Profile处于活动状态时才会在容器中注册。可以将bean分配给一个Profile,不管它是在XML中定义还是通过注释。与Profile相关的环境对象的作用是确定哪些Profile(如果有的话)当前处于活动状态,以及默认情况下哪些Profile(如果有的话)应该处于活动状态。

Properties在几乎所有的应用程序中都扮演着重要的角色,并且可以来自各种来源:属性文件、JVM系统属性、系统环境变量、JNDI、servlet上下文参数、ad-hoc属性对象、映射等等。与属性相关的环境对象的作用是为用户提供方便的服务接口,用于配置属性源和解析属性。

Environment集成了profile和properties,profile的用意接近环境,properties则是环境中的一些属性值,profile决定了当前处于哪一个环境(使用哪一种bean定义的容器),这样可以在构建工程时根据不同的阶段使用不同的环境,而不用频繁改动配置来适应不同的阶段。如开发、测试和生产环境使用的是不同的数据库配置,通过profile来对应不同的环境,无需将一个配置文件改来改去。

profile

Bean定义profile是核心容器中的一种机制,它允许在不同的环境中注册不同的Bean。

对应profile定义bean

@Profile

  • @Bean上使用
@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");
    }
}

对于@Bean方法上的@Profile,可以应用一个特殊的场景:对于重载了相同Java方法名的@Bean方法(类似于构造函数重载)的情况,需要在所有重载方法上始终声明@Profile条件。如果条件不一致,则只有重载方法中的第一个声明的条件才生效。因此,不能使用@Profile来选择具有特定参数签名的重载方法;同一bean的所有工厂方法之间的解析遵循Spring在创建时的构造函数解析算法。

  • @Configuration类上使用@Profile
@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");
    }
}

在@Configuration类上使用@Profile注解,则其内部所有的@Bean方法都遵循profile的规则,包括在类中@Import导入的config。

也可以使用@Profile作为元注解来定义一个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}

这个注解@Production和@Profile("production")意义是一样的。

XML定义profile

  • beans标签上定义
<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>
  • bean标签上定义
<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>

激活一个profile

激活一个概要文件可以通过几种方式完成,但是最简单的方法是使用通过ApplicationContext可获得的Environment API进行编程:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

此外,还可以通过系统环境变量、JVM系统属性、web.xml中的servlet上下文参数来指定spring.profiles.active属性,甚至作为JNDI中的一个条目来声明性地激活配置文件。

default Profile

@Profile("default")指定默认支持的profile。

Properties

PropertySource

Spring使用PropertySource对象来抽象环境中的properties。

    ApplicationContext ctx = new GenericApplicationContext();
    Environment env = ctx.getEnvironment();

Spring提供了env的标准实现:org.springframework.core.env.StandardEnvironment
它内部集成了系统属性和系统环境变量;

@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()));
    }
  • 添加属性到env中
    ConfigurableApplicationContext ctx = new GenericApplicationContext();
    MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
    sources.addFirst(new MyPropertySource());

@PropertySource

The @PropertySource annotation provides a convenient and declarative mechanism for adding a PropertySource to Spring’s Environment.

@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;
    }
}

参考资料

[1] Spring Framework Reference Documentation

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容