springboot注解--基础--2.1--Conditional

springboot注解–基础–2.1–Conditional


代码位置
https://gitee.com/DanShenGuiZu/learnDemo/tree/master/annotation-learn/annotation-learn1

1、介绍

  1. 只有满足条件的类A才能注册到容器中
  2. 类A必须实现Conditional接口,因为注解的value是Condition的子类或实现类

1.1、注解内容

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * 只有Condition的实现类,满足匹配条件,才能注册到容器中
	 */
	Class<? extends Condition>[] value();

}


1.2、Conditions接口

@FunctionalInterface
public interface Condition {

	/**
	 * 确定条件是否匹配,该方法决定了是否要注册相应的bean对象
	 * @param context:condition实现类的上下文
	 * @param metadata:正在检查的类或方法的元数据
	 * @return  true或者false
	 * 如果返回true:@Conditional修饰的类可以注册bean到容器中
	 * 如果返回false:@Conditional修饰的类不可以注册bean到容器中
	 */
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}


matches方法中有两个参数,参数类型分别是ConditionContext和AnnotatedTypeMetadata,
它们分别用来获取一些环境信息和注解元数据从而用在matches方法中判断是否符合条件。

1.2.1、ConditionContext

主要是跟Condition的上下文有关
用来获取Registry,BeanFactory,Environment,ResourceLoader和ClassLoader等

源码
public interface ConditionContext {
    BeanDefinitionRegistry getRegistry();
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();
    Environment getEnvironment();
    ResourceLoader getResourceLoader();
    @Nullable
    ClassLoader getClassLoader();
}

1.2.1、AnnotatedTypeMetadata

可以拿到某个注解的一些元数据,和对应的注解属性

源码
public interface AnnotatedTypeMetadata {
    boolean isAnnotated(String annotationName);
    Map<String, Object> getAnnotationAttributes(String annotationName);
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}


2、测试

2.1、代码

  

package com.example.main.Conditional.Conditional;

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

public class TestConditional implements Condition {
    
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        System.out.println("类元数据---->:" + metadata.toString());
        System.out.println("条件上下文---->:" + context.toString());
        // 如果ioc容器中有test1实例,我就注册当前类到容器中
        if (context.getRegistry().containsBeanDefinition("test1")) {
            System.out.println("------容器中存在test1实例,当前类注册到容器中-------");
            return true;
        }
        System.out.println("------容器中不存在test1实例,当前类不注册到容器中-------");
        return false;
    }
}


package com.example.main.Conditional.Conditional;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

import java.util.List;


@Configuration
public class TestConfig {
    
     @Bean 
     Test1 test1() {
     System.out.println("---------test1---------");
     return new Test1();
     }
    
    @Bean
    @Conditional(TestConditional.class)
    Test2 test2() {
        System.out.println("---------test2---------");
        return new Test2();
    }
    
    public class Test1 {
        
    }
    
    public class Test2 {
        
    }
    
    public static void main(String[] args) {


        //设置日志级别,去掉我不要的信息
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        List<Logger> loggerList = loggerContext.getLoggerList();
        loggerList.forEach(logger -> {
            logger.setLevel(Level.ERROR);
        });

        //加载上下文
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);



    }
}

2.2、结果

在这里插入图片描述

在这里插入图片描述

3、原理分析

3.1、类元数据,条件上下文debug

在这里插入图片描述

在这里插入图片描述

3.2、原理解析(debug模式)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.2.1、shouldSkip方法


// 这个方法主要是如果是解析阶段则跳过,如果是注册阶段则不跳过
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 若没有被@Conditional或其派生注解所标注,则不会跳过
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
    // 没有指定phase(阶段),注意phase可以分为PARSE_CONFIGURATION(解析阶段)或REGISTER_BEAN(注册阶段)类型
    if (phase == null) {
        // 若标有@Component,@Import,@Bean或@Configuration等注解的话,则说明是PARSE_CONFIGURATION类型
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        // 否则是REGISTER_BEAN类型
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }

    List<Condition> conditions = new ArrayList<>();
    // TODO 获得所有标有@Conditional注解或其派生注解里面的Condition接口实现类并实例化成对象。
    // 比如@Conditional(OnBeanCondition.class)则获得OnBeanCondition.class,OnBeanCondition.class实现了Condition接口
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        // 将类实例化成对象
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }
    // 排序,即按照Condition的优先级进行排序
    AnnotationAwareOrderComparator.sort(conditions);

    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            // 从condition中获得对bean是解析阶段还是注册阶段
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }
        // (若requiredPhase为null或获取的阶段类型正是当前阶段类型)且不符合condition的matches条件,则跳过
        if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
            return true;
        }
    }

    return false;
}

shouldSkip逻辑
  1. 如果是解析阶段则跳过,如果是注册阶段则不跳过;
  2. 如果是在注册阶段即REGISTER_BEAN阶段的话,此时会得到所有的Condition接口的具体实现类并实例化这些实现类,然后再执行下面关键的代码进行判断是否需要跳过。
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
    return true;
}

上面代码最重要的逻辑是调用了Condition接口的具体实现类的matches方法

若matches返回false,则跳过,不进行注册bean的操作
若matches返回true,则不跳过,进行注册bean的操作;

4、Spring的内置Condition接口实现类

Spring的Condition接口的具体实现类(Ctrl+H)

在这里插入图片描述

注意:OnBean2Condition和TestConditional是我本人自己实现的,SpringBootCondition是个抽象类。

发现Spring真正的内置的Condition接口的具体实现类只有ProfileCondition一个,非常非常少,这跟SpringBoot的大量派生条件注解形成了鲜明的对比。

ProfileCondition 跟环境有关,比如我们平时一般有dev,test和prod环境,而ProfileCondition就是判断我们项目配置了哪个环境的。

4.1、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")) {
				//通过属性值,设置Profile
				if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
					return true;
				}
			}
			return false;
		}
		return true;
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值