有时候咱们有两个配置文件, 一个是dev环境的, 一个是线上product环境的, 在开发的时候, 启动加载dev配置文件, 上线后启动应用product的配置; 这里就能够用到@Profile的性能;
其原理就是在上下文启动的时候, 通过扭转一个环境参数(-Dspring.profiles.active=xxx
)的值, 让spring去抉择加载和初始化对应的profile;
举例: 咱们有两套数据源配置: dev
和 product
; 配置区别大体如下:
- 配置文件:
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
- 配置注解文件:
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; }}
- 测试用例:
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")