多环境配置原理及运行环境控制方式
多环境的配置就是根据环境标签去适配相应环境的配置参数,好处就是“一套代码,到处运行”,不用随环境改变去修改代码。
虽然现在的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,以上就是多环境配置的原理。