Spring@Conditional详解

Posted 幽灵雾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring@Conditional详解相关的知识,希望对你有一定的参考价值。

【功能介绍】

@Conditional

给想要注入Bean增加限制条件,只有满足限制条件才会被构造并注入到Spring的IOC容器中,通常和@Bean注解一起使用。

【使用实例】

Bean类,以及注入Bean的类:

@Component
public class TestConfig 

    @Bean
    // 注入Bean之前增加限制条件:MyCondition,条件满足才会构造TestBean同时注入
    @Conditional(MyCondition.class)
    public TestBean testbean() 
        System.out.println("=====run new TestBean");
        TestBean testBean = new TestBean();
        testBean.setId(1L);
        return testBean;
    


public class TestBean 
    private Long id;

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    @Override
    public String toString() 
        return "TestBean" +
                "id=" + id +
                '';
    

自定义条件类:

public class MyCondition implements Condition 

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) 
        //context能够获取到IOC相关的信息、对象

        //获取ioc使用的beanFactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //获取当前环境信息
        Environment environment = context.getEnvironment();
        //获取bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        //metadata能取到注解的元信息
        metadata.getAnnotations().forEach(a -> 
            //注解的class
            Class<Annotation> type = a.getType();
            //注解对应的attribute
            Object value = a.getValue("value").get();
        );
        //返回false表示未满足条件,不进行构造和注入;返回true表示满足条件,正常构造和注入
        return false;
    

测试类:

@SpringBootTest
class MyConsul1ApplicationTests 

	//required=false:表示如果testBean在容器中不存在,也不会异常中断,而是单纯的testBean=null而已
	@Autowired(required=false)
	private TestBean testBean;

	@Test
	public void test() 
		System.out.println("testBean = " + testBean);
	


输出结果:

//如果MyCondition中返回true,则输出正常:
testBean = TestBeanid=1

//如果MyCondition中返回false,则输出null:
testBean = null

【源码分析】

从启动开始,选取和@Conditional有关的源码:

第一步,SpringBoot启动,并初始化applicationContext

SpringApplication.run
->
SpringApplication.createApplicationContext
->
applicationContext = new AnnotationConfigServletWebServerApplicationContext
->
applicationContext.reader = new AnnotatedBeanDefinitionReader
->
applicationContext.reader. conditionEvaluator = new ConditionEvaluator

这一步,主要是初始化applicationContext,其中包括:
用来进行注解Bean解析的reader处理器(AnnotatedBeanDefinitionReader),以及reader中的条件处理器(conditionEvaluator),如图:

第二步,applicationContext预处理

SpringApplication.prepareContext
->
SpringApplication.load
->
SpringApplication.createBeanDefinitionLoader
->
BeanDefinitionLoader.load
->
AnnotatedBeanDefinitionReader.register -> registerBean -> doRegisterBean
->
conditionEvaluator.shouldSkip
->
BeanDefinitionReaderUtils.registerBeanDefinition
->
registry.registerBeanDefinition

这一步,主要是要进行Bean的构造及注册,其中conditionEvaluator.shouldSkip决定了是否执行后续的Bean构造及注册,如图:

第三步,条件逻辑处理

这里是@Conditional注解的核心处理过程,主要就是通过回调我们自定义的Condition.matches方法来实现:

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) 
		if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) 
			return false;
		

//判断是:PARSE_CONFIGURATION还是REGISTER_BEAN
//PARSE_CONFIGURATION:代表解析配置类阶段,也就是将配置类转换为ConfigurationClass阶段
//REGISTER_BEAN:代表配置类注册为bean阶段,也就是将配置类是否需要在将其注册到IOC容器阶段
		if (phase == null) 
			if (metadata instanceof AnnotationMetadata &&
					ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) 
				return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
			
			return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
		

		List<Condition> conditions = new ArrayList<>();
		for (String[] conditionClasses : getConditionClasses(metadata)) 
			for (String conditionClass : conditionClasses) 
				Condition condition = getCondition(conditionClass, this.context.getClassLoader());
				conditions.add(condition);
			
		

		AnnotationAwareOrderComparator.sort(conditions);

		for (Condition condition : conditions) 
			ConfigurationPhase requiredPhase = null;
			if (condition instanceof ConfigurationCondition) 
				requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
			
			//这里进行matches回调,决定是否继续
			if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) 
				return true;
			
		

		return false;
	

【应用场景】

一般@Conditional用来进行Bean构造、注入的限制,直接使用@Conditional的情况并不多见,更多的是使用他的派生注解:
@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnJava等等

这些都是Spring帮我们实现了的一些常见限制条件,例如依赖某些Bean才进行注册,没有某些Bean才进行注册等等

以上是关于Spring@Conditional详解的主要内容,如果未能解决你的问题,请参考以下文章

Spring@Conditional详解

Spring @Conditional注解的使用

一文了解@Conditional注解说明和使用

(转) Java中的负数及基本类型的转型详解

详解Android WebView加载html片段

14.VisualVM使用详解15.VisualVM堆查看器使用的内存不足19.class文件--文件结构--魔数20.文件结构--常量池21.文件结构访问标志(2个字节)22.类加载机制概(代码片段