CH12-环境变量

Environment 接口介绍

在 Spring 中,Environment 接口主要管理应用程序两个方面的内容:profile 和 properties。

profile 可以简单的等同于环境,比如说平时项目中常见的环境有开发(dev)、测试(stg)和生产(prod),Spring 启动的时候可以设置激活具体的环境。当然,这个 profile 我们还可以为其赋予很多含义,这个主要看你的业务。比如说,你开发的软件会交付给客户A,也会交付给客户B,那么这个 profile 也可以定义成客户的含义

properties 是配置,配置的来源有很多,可以是配置文件、JVM 的参数、系统的环境变量、JNDI、Sevlet Contxet 参数以及 Map 对象等,使用 Environment 接口,可以方便的获取这些配置。

Bean Definition Profiles

使用 @Profile

Spring 容器可以根据不同的 profile 配置不同的 Bean。这个特性可以帮助你实现很多灵活的功能,比如:

  • 开发环境使用内存数据库,测试和生产环境才使用关系型数据库,比如 Mysql 和 Oracle 等
  • 交互给客户 A 的软件使用 A 特性,交付给客户 B 的软件使用 B 特性
@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 非常适合在两个环境下,Bean 的定义完全不一样的情况,如果两个 Bean 的定义是一样的,只是一些参数不一样的话,我们完全可以使用配置文件的方式实现。

@Profile 注解后面的表达式可以是一个简单的字符串,也可以是一个逻辑运算符。@Profile 支持如下的逻辑运算符。

  • !: A logical “not” of the profile
  • &: A logical “and” of the profiles
  • |: A logical “or” of the profiles

说明:

If a @Configuration class is marked with @Profile, all of the @Bean methods and @Import annotations associated with that class are bypassed unless one or more of the specified profiles are active. If a @Component or @Configuration class is marked with @Profile({"p1", "p2"}), that class is not registered or processed unless profiles ‘p1’ or ‘p2’ have been activated.

使用 xml 方式配置

<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

1. API 方式

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// 设置激活的 profile
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

2. 命令行方式

-Dspring.profiles.active="profile1,profile2"

3. 配置文件方式

spring:
  profiles:
    active: dev

默认的 profile

Spring 默认的 profile 是 default,可以通过 Environment 的 API 进行修改。

PropertySource 接口

PropertySource 接口是对任何形式的 key-value 键值对的抽象。

@PropertySource

@PropertySource 这个注解的作用是将配置文件中的键值对放入Environment。这个注解的作用和传统配置方式中的 context:place-hold一致。

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

经过上面这样的配置,我们就可以使用 ${key} 这种形式来取变量的值。有时如果我们没配置 key 的值,Spring 会抛异常。这时我们可以使用 ${key:defaultvalue} 这种形式配置默认值