Spring实战之JavaConfig方式多环境与profile配置

  • 什么是多环境
  • 配置profile bean-@Profile注解在类级别上
  • 开发环境配置
  • 生产环境配置
  • 配置profile bean-@Profile注解在方法级别上
  • 如何激活profile


什么是多环境

以数据源为例,在不同环境下,我们会需要代码提供不同的数据源bean。如:

  • 开发环境:我们需要嵌入式数据库
  • 生产环境:我们需要使用JNDI从容器中获取一个Datasource
  • QA环境:我们需要配置DBCP连接池获取DataSource

如果我们通过修改代码的方式每次部署不同环境时就改一次对应的代码,你是不是会疯掉呢?(会!)
那怎么做能根据不同的环境来让我们的代码自动提供相应的数据源对象呢?

配置profile bean-@Profile注解在类级别上

通过Spring为环境相关的bean所提供的解决方案,是根据环境来决定该创建那个bean和不该创建那个bean。要注意的是,Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定

Spring引入了bean profile的功能。要使用profile,首先要将所有不同的bean定义到一个或多个profile之中,在将应用部署到每个环境时,还要确保对应的profile处于激活(active)的状态。

在Java配置中,可以使用@Profile注解指定某个bean属于哪一个profile。

开发环境配置

例如,在配置类中,嵌入式数据库的DataSource可能会配置成如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;

import javax.sql.DataSource;


@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }
}

类上添加了@Profile注解,它会告诉Spring这个配置类中 的bean只有在dev profile激活时才会创建。如果dev profile 没有激活,那么该类中带有@Bean注解的方法都会被忽略。

生产环境配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jndi.JndiObjectFactoryBean;

import javax.sql.DataSource;


@Configuration
@Profile("prod")
public class ProductionProfileConfig {
    @Bean
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/myDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

类上添加了@Profile注解,它会告诉Spring这个配置类中 的bean只有在prod profile激活时才会创建。

配置profile bean-@Profile注解在方法级别上

上面说了@Profile注解在类级别上,现在我们看看将@Profile注解在方法级别上,可以将多个bean的声明放到同一个配置类之中,更方便的进行代码阅读。如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.jndi.JndiObjectFactoryBean;

import javax.sql.DataSource;


@Configuration
@Profile("prod")
public class DataSourceConfig {
    /**
     * 为dev profile装配的bean
     */
    @Bean
    @Profile("dev")
    public DataSource embeddedDataSource() {
        return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .addScript("classpath:schema.sql")
                .addScript("classpath:test-data.sql")
                .build();
    }

    /**
     * 为prod profile装配的bean
     */
    @Bean
    @Profile("prod")
    public DataSource jndiDataSource() {
        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
        jndiObjectFactoryBean.setJndiName("jdbc/myDS");
        jndiObjectFactoryBean.setResourceRef(true);
        jndiObjectFactoryBean.setProxyInterface(DataSource.class);
        return (DataSource) jndiObjectFactoryBean.getObject();
    }
}

但是需要注意一点,尽管如上每个Datasource bean都被声明在一个配置类中,并且只有当规定的profile激活时,相应的bean才会被创建,但是在代码不断迭代开发的过程中,可能会有其他的bean并没有被开发人员声明在一个给定的profile范围内。对于没有指定profile的bean,它始终都会被创建,此类bean就与激活哪个profile没有关系了!

如何激活profile

Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:
spring.profiles.activespring.profiles.default

  • 如果设置了spring.profiles.active属性的话,那它的值就用来确定激活哪个profile;
  • 如果没有设置spring.profiles.active,Spring将会查找spring.profiles.default的值来激活profile;
  • 如果都没有设置,那就意味着没有激活的profile,因此上述代码中的Datasource bean都不会创建,只会创建没有定义在profile中的bean!

有多种方式来设置这2个属性:

  • 作为DispatcherServlet的初始化参数
  • 作为Web应用的上下文参数
  • 作为JNDI条目
  • 作为环境变量
  • 作为JVM的系统属性
  • 在集成测试类上,使用@ActiveProfiles注解设置
  • 等等。。