Spring中Profiles的作用及原理浅析
# 什么是Profile
profile是用户区别环境来执行那些代码的,在我们日常开发中经常需要由开发环境到生产环境切换,这个时候需要更改数据库、redis等等这些的连接地址和密码,改完再打包,然后再改回来,这样实在是繁琐且多余。而Profile的作用正是解决这个问题的。
# Spring中使用Profile的例子
- 创建根据Profile配置的配置类
public class Message {
private String message;
}
@Configuration
@Profile("dev")
public class DevConfig {
@Bean
Message message() {
Message message = new Message();
message.setMessage("dev env");
return message;
}
}
@Configuration
@Profile("pro")
public class ProConfig {
@Bean
Message message() {
Message message = new Message();
message.setMessage("pro env");
return message;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- 测试根据激活的Profile来配置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = ContextConfig.class)
@ActiveProfiles("pro")
public class SpringTest {
@Autowired
private Message message;
@Test
public void testProfile(){
System.out.println(message.getMessage());
}
}
2
3
4
5
6
7
8
9
10
11
12
其中ContextConfiguration是指定包扫描的,里面就配置了ComponetScan @ActiveProfile是指定激活的Profile,值可以是数组 比如同时激活两个环境@ActiveProfile({"dev","pro"})
- 运行代码测试
# 浅析源码
用户标记在什么环境下启用,使用实示例@Profile("dev")。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
2
3
4
5
6
7
8
条件匹配,当满足条件的时候才使这个配置生效。value也得以证明是一个数组,可以传入多个profile,可以看到里面有一个@Conditional注解,可以跟进Conditional的源码看看
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional{
Class<? extends Condition>[] value();
}
2
3
4
5
6
里面的需要传入实现了Condition接口,而再@Profile中的@Conditional里面传入了ProfileCondition.class,可以再看看ProfileCondition的源码
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
他实现了Condition接口,ConditionContext里面可以拿到当前环境的激活的Profile然后从AnnotatedTypeMetadata中拿到@Profile注解的value值,然后对比是否包含当前环境的Profile,如果有则返回true,条件生效。
# 激活Profile
激活方式是设置spring.profiles.active的值,可以是一个数组。也可以设置spring.profiles.default来指定默认激活的profile。 通常我们可以通过一下几种方式来改变profile
- 在spring-web中通过DispatcherServlet传入spring.profiles.default,比如:
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
2
3
4
5
6
7
8
在DispatcherServlet中可以通过super.getServletConfig().getInitParameter("spring.profiles.default")获取
- 在web应用中使用context上下问参数来指定
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
2
3
4
在Servlet中可以通过getServletConfig().getServletContext().getInitParameter("spring.profiles.default")获取。
- 作为JNDI条目,JDNI是Java 命名与目录接口(Java Naming and Directory Interface)
- 作为环境变量,设置一个名字为spring.active.profile的环境变量
- 作为JVM的参数,比如java -jar xxx.jar --spring.profile.active=dev
- 在测试类中可以使用@ActiveProfiles注解指定