Spring @Profile 和 @Conditional 注解

@Profile@Conditional 都可以根据条件加载指定的Bean。@Profile主要使用场景是用来跟环境绑定,不同的环境加载不同的Bean;@Conditional可用定时更详细的加载条件。

本文依赖的Spring版本

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>

配置类如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package spring4.profile;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class ProfileConfig {

@Bean("text")
@Profile("dev")
public String devText() {
return "dev";
}

@Bean("text")
@Profile("rel")
public String relText() {
return "rel";
}
}

@Configuration 注解标识这是一个配置类;
@Bean注解定义一个托管给Spring的Bean,Bean 的 ID 都是text;
@Profile将Bean与指定环境绑定,dev环境将得到”dev”字符串,rel环境将得到”rel”字符串;

如何激活Profile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package spring4;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("rel");
context.scan("spring4");
context.refresh();

String text = context.getBean("text", String.class);
System.out.println(text);
}
}

这时候控制台将打印 rel,如果指定为dev(context.getEnvironment().setActiveProfiles("dev");),将会打印dev。

没有指定Profile的Bean

在 ProfileConfig 类的最上面(dev和rel之前)配置一个没有指定任何 Profile 的Bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
public class ProfileConfig {

@Bean("text")
public String defaultText() {
return "no profile";
}

@Profile("dev")
@Bean("text")
...
@Profile("rel")
@Bean("text")
...
}

这时候不管是激活dev还是rel,或者不激活任何Profile,都会打印 no profile,如何在不激活任何Profile的时候才输出no profile呢? 这里先使用条件注解的方式。

条件注解

创建一个实现Condition接口的类,逻辑是如果没有激活任何profile,返回true。

1
2
3
4
5
6
7
8
9
10
11
12
13
package spring4.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DefaultProfileCondition implements Condition {

@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getActiveProfiles().length <= 0;
}
}

defaultText()方法上新增条件注解,如下

1
2
3
4
5
@Bean("text")
@Conditional(DefaultProfileCondition.class)
public String defaultText() {
return "no profile";
}

这时候,只有在没有激活任何Profile,才会输出”no profile”。

默认Profile

以上为了引出条件注解,实际上绕了弯子,实现默认Profile实际上只需要@Profile("default")

详见 org.springframework.core.env.AbstractEnvironment 类,里面定义了一个default常量,呵呵

1
2
3
4
5
6
7
8
9
10
/**
* Return the set of reserved default profile names. This implementation returns
* {@value #RESERVED_DEFAULT_PROFILE_NAME}. Subclasses may override in order to
* customize the set of reserved names.
* @see #RESERVED_DEFAULT_PROFILE_NAME
* @see #doGetDefaultProfiles()
*/
protected Set<String> getReservedDefaultProfiles() {
return Collections.singleton(RESERVED_DEFAULT_PROFILE_NAME);
}

PS

本文涉及到的注解对应的Spring 版本

注解 Spring版本
@Configuration 3.0
@Bean 3.0
@Profile 3.1
@Conditional 4.0

单元测试如何激活Profile

使用 @ActiveProfiles("dev") 即可

拓展阅读

Spring高级话题-条件注解-@Condition
Spring常用配置-Profile

@Profile
Conditionally include @Configuration classes or @Bean methods