1.@Profile注解
主要的实现逻辑是通过@Conditional实现,@Profile是一种粗粒度的条件控制并只针对于环境条件配置,匹配的方法在ProfileCondition.java中,实现原理下面讲解.
2.@Conditional注解
使用场景:用于满足一定条件之后对指定的类进行加载到容器;比如说:对于是否启用日志打印功能,是否对指定类型请求进行过滤等等细粒度的控制;实现的方式同@Profile,具体步骤如下(以@Profile为例说明):
2.1 自定义注解(@Profile主要作用在于指定环境加载指定的对象到容器中)
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* 该注解支持的属性数组中指定环境值,根据配置的环境标识进行注册到容器中(比如说value=dev一般表示从开发环境中需要进行注册)
*/
String[] value();
}
2.2 自定义条件类,设置匹配逻辑(ProfileCondition.java 需要实现Condition的matches实现匹配逻辑)
class ProfileCondition implements Condition {
// 从已启动容器中的环境信息(比如说test、dev、pro、default)匹配标注@Profile注解的属性值是否一致,如果一致则加载到容器中,否则不加载到容器中
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取Profile注解中的所有属性值,以key-value的方式存储到attrs中
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
// 判断当前容器是否与注解Profiles中的属性值匹配
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
2.3 使用注解(开发环境中此对象才会进行注入到容器中)
@Component
@Profile("dev")
public class ABean {
private int age;
private String name;
}
3.@Conditional的衍生注解:
@ConditionalOnProperty:如果有指定的配置,条件生效;
@ConditionalOnBean:如果有指定的Bean,条件生效;
@ConditionalOnMissingBean:如果没有指定的Bean,条件生效;
@ConditionalOnMissingClass:如果没有指定的Class,条件生效;
@ConditionalOnWebApplication:在Web环境中条件生效;
@ConditionalOnExpression:根据表达式判断条件是否生效。
以springboot中最常用的@ConditionalOnProperty为例讲解:
场景:根据配置文件中spring.personal.object的配置true或是false决定PersonalBean是否加载到容器中.
PersonalBean.java
@Component
@ConditionalOnProperty(value = "spring.personal.object",matchIfMissing = true)
// matchIfMissing = true 表示如果条件属性值没有配置,默认true进行匹配
public class PersonalBean {
public PersonalBean() {
System.out.println("自定义bean创建");
}
}
配置文件中内容:
spring:
# 指定条件设置加载自定义bean
personal:
object: true
配置文件中配置true时控制台打印信息:
@ConditionalOnProperty注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
}
关键看一下OnPropertyCondition.java实现逻辑:
@Order(Ordered.HIGHEST_PRECEDENCE + 40)
class OnPropertyCondition extends SpringBootCondition {
// 获取匹配结果对象
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 将@ConditionalOnProperty属性进行数据转换,转化为AnnotationAttributes集合,转化后的集合:
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
// 获取匹配结果,主要是用于上层包扫描是否跳过添加到candidate(待加载bean定义对象集合)中
ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
// 省略部分代码..............
// 获取最终的匹配结果,ConditionOutcome属性:match==true,message=@ConditionalOnProperty (spring.personal.object)
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver) {
Spec spec = new Spec(annotationAttributes);
List<String> missingProperties = new ArrayList<>();
List<String> nonMatchingProperties = new ArrayList<>();
spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
if (!missingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property", "properties").items(Style.QUOTE, missingProperties));
}
if (!nonMatchingProperties.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.found("different value in property", "different value in properties")
.items(Style.QUOTE, nonMatchingProperties));
}
return ConditionOutcome
.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).because("matched"));
}
// 静态内部类实例化
private static class Spec {
private final String prefix;
private final String havingValue;
private final String[] names;
private final boolean matchIfMissing;
Spec(AnnotationAttributes annotationAttributes) {
String prefix = annotationAttributes.getString("prefix").trim();
if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
prefix = prefix + ".";
}
this.prefix = prefix;
this.havingValue = annotationAttributes.getString("havingValue");
// 获取@ConditionalOnProperty中name或是value的属性值,如果value不为空返回value的属性值,如果value为空返回name的属性值
this.names = getNames(annotationAttributes);
this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
}
// 获取@ConditionalOnProperty中name或是value的属性值,如果value不为空返回value的属性值,如果value为空返回name的属性值
private String[] getNames(Map<String, Object> annotationAttributes) {
String[] value = (String[]) annotationAttributes.get("value");
String[] name = (String[]) annotationAttributes.get("name");
Assert.state(value.length > 0 || name.length > 0,
"The name or value attribute of @ConditionalOnProperty must be specified");
Assert.state(value.length == 0 || name.length == 0,
"The name and value attributes of @ConditionalOnProperty are exclusive");
return (value.length > 0) ? value : name;
}
// 根据@ConditionalOnProperty中name属性值或是values属性值读取
private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching) {
for (String name : this.names) {
String key = this.prefix + name;
if (resolver.containsProperty(key)) {
if (!isMatch(resolver.getProperty(key), this.havingValue)) {
nonMatching.add(name);
}
}
else {
if (!this.matchIfMissing) {
missing.add(name);
}
}
}
}
// 省略部分代码..............................
}
}
@ConditionalOnProperty涉及调用流程:
涉及类:
ClassPathBeanDefinitionScanner.java
ConditionEvaluator.java
OnPropertyCondition.java