Spring的@Conditional详解

 更新时间:2024年01月03日 10:27:23   作者:猿人林克  
这篇文章主要介绍了Spring的@Conditional详解,给想要注入Bean增加限制条件,只有满足限制条件才会被构造并注入到Spring的IOC容器中,通常和@Bean注解一起使用,需要的朋友可以参考下

功能介绍

@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 = TestBean{id=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详解的文章就介绍到这了,更多相关@Conditional注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot+Nginx+MySQL容器化实战指南

    Spring Boot+Nginx+MySQL容器化实战指南

    这篇文章主要介绍了Spring Boot+Nginx+MySQL容器化实战指南,本文通过思路代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2026-03-03
  • java编程scanner类用法示例

    java编程scanner类用法示例

    这篇文章主要介绍了java编程scanner类用法示例,涉及一个通过scanner类实现需要手动输入变量时进行输入的实例,然后分享了一个简单的eclipse对Java代码格式化的技巧,具有一定借鉴价值,需要的朋友可以参考。
    2017-11-11
  • Spring Boot/Spring Cloud 应用日志书写详细实例指南

    Spring Boot/Spring Cloud 应用日志书写详细实例指南

    这篇文章主要介绍了Spring Boot/Spring Cloud 应用日志书写详细指南,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2026-03-03
  • 详解如何使用Mybatis的拦截器

    详解如何使用Mybatis的拦截器

    MyBatis 拦截器是 MyBatis 提供的一个强大特性,它允许你在 MyBatis 执行其核心逻辑的关键节点插入自定义逻辑,从而改变 MyBatis 的默认行为,本文给大家详细介绍了如何使用Mybatis的拦截器,需要的朋友可以参考下
    2024-03-03
  • Eclipse 开发java 出现Failed to create the Java Virtual Machine错误解决办法

    Eclipse 开发java 出现Failed to create the Java Virtual Machine错误

    这篇文章主要介绍了Eclipse 开发java 出现Failed to create the Java Virtual Machine错误解决办法的相关资料,需要的朋友可以参考下
    2017-04-04
  • 使用Spring实现@Value注入静态字段

    使用Spring实现@Value注入静态字段

    这篇文章主要介绍了使用Spring实现@Value注入静态字段方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • Kotlin常用函数let,with,run,apply用法与区别案例详解

    Kotlin常用函数let,with,run,apply用法与区别案例详解

    这篇文章主要介绍了Kotlin常用函数let,with,run,apply用法与区别案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • Java使用Spire.Doc实现Word转PDF的完整方案

    Java使用Spire.Doc实现Word转PDF的完整方案

    在OA系统开发、电子合同生成等场景中,Java开发者在处理文档自动化时最常遇到的痛点:生成的Word文档通过传统POI转换PDF时格式丢失,本文介绍的Spire.Doc for Java方案能保持原版格式,实现Word转PDF,文中通过代码讲解的非常详细,需要的朋友可以参考下
    2025-08-08
  • java 内部类的实例详解

    java 内部类的实例详解

    这篇文章主要介绍了java 内部类的实例详解的相关资料,希望通过本文大家能够理解掌握java内部类的使用,需要的朋友可以参考下
    2017-09-09
  • Mybatis迁移到Mybatis-Plus的实现方法

    Mybatis迁移到Mybatis-Plus的实现方法

    这篇文章主要介绍了Mybatis迁移到Mybatis-Plus的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08

最新评论