有时候咱们有两个配置文件, 一个是dev环境的, 一个是线上product环境的, 在开发的时候, 启动加载dev配置文件, 上线后启动应用product的配置; 这里就能够用到@Profile的性能;

其原理就是在上下文启动的时候, 通过扭转一个环境参数(-Dspring.profiles.active=xxx)的值, 让spring去抉择加载和初始化对应的profile;

举例: 咱们有两套数据源配置: devproduct; 配置区别大体如下:

  1. 配置文件: src\main\resources\db.properties
dev.mysql.url = jdbc:mysql://127.0.0.1/devdev.mysql.username = niewjdev.mysql.password = 123456dev.mysql.validationQuery = SELECT 1#-----------------product.mysql.url = jdbc:mysql://niewj.com/productproduct.mysql.username = rootproduct.mysql.password = 654321product.mysql.validationQuery = SELECT 1
  1. 配置注解文件: ProfilesDatasourceConfig.java
package com.niewj.config;import com.alibaba.druid.pool.DruidDataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.context.annotation.PropertySource;@Configuration@PropertySource("classpath:/db.properties")public class ProfilesDatasourceConfig {    /**     * product 配置     */    @Value("${product.mysql.url}")    private String urlProduct;    @Value("${product.mysql.username}")    private String usernameProduct;    @Value("${product.mysql.password}")    private String passwordProduct;    /**     * dev 配置     */    @Value("${dev.mysql.url}")    private String urlDev;    @Value("${dev.mysql.username}")    private String usernameDev;    @Value("${dev.mysql.password}")    private String passwordDev;    /**     * 其余通用配置     */    @Value("${dev.mysql.validationQuery}")    private String validationQuery = "SELECT 1";    @Profile("dev")    @Bean    public DruidDataSource dataSourceDev(){        DruidDataSource ds = new DruidDataSource();        ds.setUrl(urlDev);        ds.setUsername(usernameDev);        ds.setPassword(passwordDev);        ds.setValidationQuery(validationQuery);        System.out.println("初始化-dev-druidDataSource");        return ds;    }    @Profile("product")    @Bean    public DruidDataSource dataSourceProduct(){        DruidDataSource ds = new DruidDataSource();        ds.setUrl(urlProduct);        ds.setUsername(usernameProduct);        ds.setPassword(passwordProduct);        ds.setValidationQuery(validationQuery);        System.out.println("初始化-product-druidDataSource");        return ds;    }}
  1. 测试用例: src\test\java\com\niewj\ProfileTest.java
package com.niewj;import com.alibaba.druid.pool.DruidDataSource;import com.niewj.config.ProfilesDatasourceConfig;import org.junit.Test;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.stream.Stream;/** * spring profile测试 */public class ProfileTest {    @Test    public void testProfile() {        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();        ctx.register(ProfilesDatasourceConfig.class);        ctx.getEnvironment().setActiveProfiles("product"); // 1        // ctx.getEnvironment().setActiveProfiles("dev"); // 2        ctx.refresh();        // 打印spring容器中的 BeanDefinition        Stream.of(ctx.getBeanDefinitionNames()).forEach(e -> System.out.println(e));        System.out.println("=============================");        DruidDataSource ds = null;        if ((ds = ctx.getBean(DruidDataSource.class)) != null) {            System.out.println(ds);        }        ctx.close();    }}

这里有两种形式能够实现调用:

1. 批改spring上下文的Environment参数

结构器初始化时不传入加载配置类; 而是初始化后, 上下文手动注册配置类, 而后批改active profiles, 最初refresh上下文;

其实传入配置类的结构器的源码如下: 咱们如果应用它, 就无奈批改环境参数并refresh了, 所以注册的事件咱们本人来干!

    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {        this();        register(componentClasses);        refresh();    }

咱们这么操作, 输入如下:

output: 可见是加载了 product的profile; 同样 换了 dev亦然!

初始化-product-druidDataSourceorg.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.event.internalEventListenerProcessororg.springframework.context.event.internalEventListenerFactoryprofilesDatasourceConfigdataSourceProduct============================={    CreateTime:"2020-07-17 17:11:46",    ActiveCount:0,    PoolingCount:0,    CreateCount:0,    DestroyCount:0,    CloseCount:0,    ConnectCount:0,    Connections:[    ]}七月 17, 2020 5:11:46 下午 com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl info信息: {dataSource-0} closing ...

2. 执行加参数-Dspring.profiles.active=product

应用结构器注册配置类, 而后执行的时加参数-Dspring.profiles.active=product来触发调用, 达到目标;

(1). 结构器初始化加载配置类:

    @Test    public void testProfileConstructure() {        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ProfilesDatasourceConfig.class);        // 打印spring容器中的 BeanDefinition        Stream.of(ctx.getBeanDefinitionNames()).forEach(e -> System.out.println(e));        System.out.println("=============================");        DruidDataSource ds = null;        if ((ds = ctx.getBean(DruidDataSource.class)) != null) {            System.out.println(ds);        }        ctx.close();    }

(2). 执行时加参数 -Dspring.profiles.active=product , 这种形式 输入也和下面一样的, output 略去

3. 小结:

(1). deault: 多个 @Profile("dev") @Profile("product") , 如果执行时没有手动加参数: -Dspring.profiles.active=xxx 默认只会寻找@Profile("default")的; 没有的话, 就都不会在容器中初始化;

(2). 如果@Profile("dev")是在配置类上注解的, 类下所有的操作只有在匹配到执行时 -Dspring.profiles.active=dev时, 才会初始化, 否则, 甚至连配置类自身都不会初始化注册到容器;

(3). 顺着(2)的阐明, 如果执行时-Dspring.profiles.active=dev, 类上的注解也是@Profile("dev"), 此配置类中没有@Profile注解的 @Bean办法都会失常初始化注册到容器; 只有不匹配的@Profile("xxx")不会注册;

(4). ctx.getEnvironment().setActiveProfiles("product"),能够设置多个: ctx.getEnvironment().setActiveProfiles("product", "dev")