多环境配置原理及运行环境控制方式

2022-06-30 10:08:25

多环境的配置就是根据环境标签去适配相应环境的配置参数,好处就是“一套代码,到处运行”,不用随环境改变去修改代码。

虽然现在的springboot已经可以很好的支持多环境配置了,但是背后的原理还是要知道的。

那大致的想下,怎么实现的呢?

概括的说,其实很简单,就是那些配置类的bean是根据不同的环境标签动态注入spring容器实现的。

也就是说,根据环境标签,去注入和这个环境对应的配置类的bean到Spring容器。

一、@Profile

spring中使用@Profile注解去给bean添加一个环境标签,我们先看不使用该注解时会怎样:

假设SomeConfig是某个配置类,类似DataSource数据配置类,不同的环境有不同的值。

@Data
public class SomeConfig {
    private String url;
    private String port;
}
@Configuration
public class ProfileConfig {

    @Bean
    public SomeConfig someConfigDev(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://1");
        someConfig.setPort("666");
        return someConfig;
    }

    @Bean
    public SomeConfig someConfigTest(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://2");
        someConfig.setPort("667");
        return someConfig;
    }

    @Bean
    public SomeConfig someConfigProd(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://3");
        someConfig.setPort("668");
        return someConfig;
    }
}

打印容器中所有的bean:

    public void test3() {
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Stream.of(beanDefinitionNames).forEach(System.out::println);
    }
someConfigDev
someConfigTest
someConfigProd

可以看到不使用@Profile注解的bean是在任何环境下都加载的。

这里的不使用@Profile注解那这里面的三个SomeConfig类型的bean都注入了spring容器,那肯定不行啊,到底该用哪一个呢?实际中,每个环境中只能生成一个SomeConfig类型的bean。

加上@Profile注解:

@Configuration
public class ProfileConfig01 {

    @Profile("dev")
    @Bean
    public SomeConfig someConfigDev(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://1");
        someConfig.setPort("666");
        return someConfig;
    }

    @Profile("test")
    @Bean
    public SomeConfig someConfigTest(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://2");
        someConfig.setPort("667");
        return someConfig;
    }

    @Profile("prod")
    @Bean
    public SomeConfig someConfigProd(){
        SomeConfig someConfig = new SomeConfig();
        someConfig.setUrl("http://3");
        someConfig.setPort("668");
        return someConfig;
    }
}

在启动服务时,设置下环境标签-Dspring.profiles.active=prod

结果只会有一个prod环境的SomeConfig类型的bean:

someConfigProd

这里是因为指定了环境标签是prod,因此只有注解为@Profile(“prod”)修饰的bean会被注入spring容器,而其他的bean不会注入。

二、环境控制

上面说了一种控制方式,就是在启动参数里添加了-Dspring.profiles.active=prod

还有一种代码的方式,就是直接在spring容器创建以后,通过它包含的环境对象去设置。

    @Test
    public void test9() {
        // 1.创建容器,这里只是创建容器,没有指定配置类,是为了先设置环境再载入bean
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 2.设置环境标签
        applicationContext.getEnvironment().setActiveProfiles("prod", "dev");
        // 3.注册主配置类
        applicationContext.register(ProfileConfig01.class);
        // 4.刷新容器配置
        applicationContext.refresh();
    }
21:35:02.069 [main] DEBUG org.springframework.core.env.StandardEnvironment - Activating profiles [prod, dev]
21:35:02.105 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1b083826
org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'profileConfig01'
21:35:02.352 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'someConfigDev'
21:35:02.368 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'someConfigProd'

可以看到,这里使用代码的方式指定了环境标签,只有prod、dev对应的bean注入了容器。

需要注意的是:创建容器时不能直接指定配置类,需要先创建容器再指定配置环境最后注册配置类刷新容器。若一开始创建容器时就指定了配置类,那配置类里面包含的bean就会在环境配置前已经注入spring容器。

ok,以上就是多环境配置的原理。

  • 作者:沙滩de流沙
  • 原文链接:https://imok520.blog.csdn.net/article/details/117000965
    更新时间:2022-06-30 10:08:25