spring注解驱动开发-(9)-@Profile-多配置文件-容器的注册选择

1. 场景描述

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

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

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

2. 代码

2.1 pom依赖增加:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.22</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.6</version>
        </dependency>

2.2. 配置文件: src\main\resources\db.properties

dev.mysql.url = jdbc:mysql://127.0.0.1/dev
dev.mysql.username = niewj
dev.mysql.password = 123456
dev.mysql.validationQuery = SELECT 1

#-----------------
product.mysql.url = jdbc:mysql://niewj.com/product
product.mysql.username = root
product.mysql.password = 654321
product.mysql.validationQuery = SELECT 1

2.3. 配置注解文件: 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;
    }
}

2.4. 测试用例:

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();
    }

}

3. 调用方式:

3.1. 修改spring上下文的Environment参数

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

其实传入配置类的构造器的源码如下: 我们如果使用它, 就无法修改环境参数并refresh了, 所以注册的事情我们自己来干!

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

我们这么操作, 输出如下:

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

初始化-product-druidDataSource
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
profilesDatasourceConfig
dataSourceProduct
=============================
{
    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 ...

3.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 略去

image.png

4. 小结:

(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")


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com

×

喜欢就点赞,疼爱就打赏