《深入浅出Spring》Spring注解@Conditional

@Conditional注解

@Conditional注解是从spring4.0才有的,可以用在任何类型或者方法上面,通过@Conditional注解可以配置一些条件判断,当所有条件都满足的时候,被@Conditional标注的目标才会被spring容器处理。
@Conditional源码:

@Target({
   ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
   
    Class<? extends Condition>[] value();
}

这个注解只有一个value参数,Condition类型的数组,Condition是一个接口,表示一个条件判断,内部有个方法返回true或false,当所有Condition都成立的时候,@Conditional的结果才成立。

Condition接口

用来表示条件判断的接口,源码如下:

@FunctionalInterface
public interface Condition {
   
    /**
     * 判断条件是否匹配
     * context:条件判断上下文
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

是一个函数式接口,内部只有一个matches方法,用来判断条件是否成立的,2个参数:

  1. context:条件上下文,ConditionContext接口类型的,可以用来获取容器中的个人信息
  2. metadata:用来获取被@Conditional标注的对象上的所有注解信息

ConditionContext接口

这个接口中提供了一些常用的方法,可以用来获取spring容器中的各种信息,看一下源码:

public interface ConditionContext {
   
    /**
     * 返回bean定义注册器,可以通过注册器获取bean定义的各种配置信息
     */
    BeanDefinitionRegistry getRegistry();
    /**
     * 返回ConfigurableListableBeanFactory类型的bean工厂,相当于一个ioc容器对象
     */
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();
    /**
     * 返回当前spring容器的环境配置信息对象
     */
    Environment getEnvironment();
    /**
     * 返回资源加载器
     */
    ResourceLoader getResourceLoader();
    /**
     * 返回类加载器
     */
    @Nullable
    ClassLoader getClassLoader();
}
  • 比较关键性的问题:条件判断在什么时候执行?
    Spring对配置类的处理主要分为2个阶段:
  1. 配置类解析阶段
    会得到一批配置类的信息,和一些需要注册的bean

  2. bean注册阶段
    将配置类解析阶段得到的配置类和需要注册的bean注册到spring容器中

  • 看一下什么是配置类
    类中有下面任意注解之一的就属于配置类:

类上有@Compontent注解
类上有@Configuration注解
类上有@CompontentScan注解
类上有@Import注解
类上有@ImportResource注解
类中有@Bean标注的方法

判断一个类是不是一个配置类,是否的是下面这个方法,有兴趣的可以看一下:

org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate

spring中处理这2个过程会循环进行,直到完成所有配置类的解析及所有bean的注册。

Spring对配置类处理过程
源码位置:

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

整个过程大致的过程如下:

  1. 通常我们会通过new AnnotationConfigApplicationContext()传入多个配置类来启动spring容器

  2. spring对传入的多个配置类进行解析

  3. 配置类解析阶段:这个过程就是处理配置类上面6中注解的过程,此过程中又会发现很多新的配置类,比如@Import导入的一批新的类刚好也符合配置类,而被@CompontentScan扫描到的一些类刚好也是配置类;此时会对这些新产生的配置类进行同样的过程解析

  4. bean注册阶段:配置类解析后,会得到一批配置类和一批需要注册的bean,此时spring容器会将这批配置类作为bean注册到spring容器,同样也会将这批需要注册的bean注册到spring容器

  5. 经过上面第3个阶段之后,spring容器中会注册很多新的bean,这些新的bean中可能又有很多新的配置类

  6. Spring从容器中将所有bean拿出来,遍历一下,会过滤得到一批未处理的新的配置类,继续交给第3步进行处理

  7. step3到step6,这个过程会经历很多次,直到完成所有配置类的解析和bean的注册
    从上面过程中可以了解到:

  8. 可以在配置类上面加上@Conditional注解,来控制是否需要解析这个配置类,配置类如果不被解析,那么这个配置上面6种注解的解析都会被跳过

  9. 可以在被注册的bean上面加上@Conditional注解,来控制这个bean是否需要注册到spring容器中

  10. 如果配置类不会被注册到容器,那么这个配置类解析所产生的所有新的配置类及所产生的所有新的bean都不会被注册到容器

一个配置类被spring处理有2个阶段:

  • 配置类解析阶段、
  • bean注册阶段(将配置类作为bean被注册到spring容器)。

如果将Condition接口的实现类作为配置类上@Conditional中,那么这个条件会对两个阶段都有效,此时通过Condition是无法精细的控制某个阶段的,如果想控制某个阶段,比如可以让他解析,但是不能让他注册,此时就就需要用到另外一个接口了:ConfigurationCondition

ConfigurationCondition接口

看一下这个接口的源码:

public interface ConfigurationCondition extends Condition {
   
    /**
     * 条件判断的阶段,是在解析配置类的时候过滤还是在创建bean的时候过滤
     */
    ConfigurationPhase getConfigurationPhase();
    /**
     * 表示阶段的枚举:2个值
     */
    enum ConfigurationPhase {
   
        /**
         * 配置类解析阶段,如果条件为false,配置类将不会被解析
         */
        PARSE_CONFIGURATION,
        /**
         * bean注册阶段,如果为false,bean将不会被注册
         */
        REGISTER_BEAN
    }
}

ConfigurationCondition接口相对于Condition接口多了一个getConfigurationPhase方法,用来指定条件判断的阶段,是在解析配置类的时候过滤还是在创建bean的时候过滤。

@Conditional使用的3步骤

  1. 自定义一个类,实现Condition或ConfigurationCondition接口,实现matches方法
  2. 在目标对象上使用@Conditional注解,并指定value的值为自定义的Condition类型
  3. 启动spring容器加载资源,此时@Conditional就会起作用了

栗子

1. 阻止配置类的处理

在配置类上面使用@Conditional,这个注解的value指定的Condition当有一个为false的时候,spring就会跳过处理这个配置类。

自定义一个Condition类:

package com.yuan11.annotation;

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

/**
 * @title: MyCondition1
 * @Author yuan11
 * @Date: 2022/6/27 23:57
 * @Version 1.0
 */
public class MyCondition1 implements Condition {
   
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
   
        return false;
    }
}

matches方法内部我们可以随意发挥,此处为了演示效果就直接返回false。

来个配置类,在配置类上面使用上面这个条件,此时会让配置类失效,如下:

package com.yuan11.annotation;

import org
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值