通过条件来控制bean的注册

@Conditional注解

可以通过@Conditional来控制bean是否需要注册,控制被@Configuration标注的配置类是否需要被解析等。

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

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

Spring对配置类的处理主要分为2个阶段:

  1. 配置类解析阶段
    会得到一批配置类的信息,和一些需要注册的bean
  2. bean注册阶段
    将配置类解析阶段得到的配置类和需要注册的bean注册到spring容器中

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

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

判断一个类是不是一个配置类,是否的是下面这个方法,可以看一下:org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate

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的注册

从上面过程中可以了解到

  1. 可以在配置类上面加上@Conditional注解,来控制是否需要解析这个配置类,配置类如果不被解析,那么这个配置上面6种注解的解析都会被跳过
  2. 可以在被注册的bean上面加上@Conditional注解,来控制这个bean是否需要注册到spring容器中
  3. 如果配置类不会被注册到容器,那么这个配置类解析所产生的所有新的配置类及所产生的所有新的bean都不会被注册到容器

一个配置类被spring处理有2个阶段:配置类解析阶段、bean注册阶段(将配置类作为bean被注册到spring容器)。

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

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

@Conditional使用的3步骤

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

自定义的注解上面也可以使用@Conditional注解

指定Condition的顺序
自定义的Condition可以实现PriorityOrdered接口或者继承Ordered接口,或者使用@Order注解,通过这些来指定这些Condition的优先级。
排序规则:先按PriorityOrdered排序,然后按照order的值进行排序;也就是:PriorityOrdered asc,order值 asc

ConfigurationCondition类
判断bean存不存在的问题,通常会使用ConfigurationCondition这个接口,阶段为:REGISTER_BEAN,这样可以确保条件判断是在bean注册阶段执行的。

对springboot比较熟悉的,它里面有很多@Conditionxxx这样的注解,可以去看一下这些注解,很多都实现了ConfigurationCondition接口。

@Profile 的定义,判断条件在 ProfileCondition 类中

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
	String[] value();

}

@Conditional注解是被下面这个类处理的
org.springframework.context.annotation.ConfigurationClassPostProcessor

总结

  1. @Conditional注解可以标注在spring需要处理的对象上(配置类、@Bean方法),相当于加了个条件判断,通过判断的结果,让spring觉得是否要继续处理被这个注解标注的对象
  2. spring处理配置类大致有2个过程:解析配置类、注册bean,这两个过程中都可以使用@Conditional来进行控制spring是否需要处理这个过程
  3. Condition默认会对2个过程都有效
  4. ConfigurationCondition控制得更细一些,可以控制到具体那个阶段使用条件判断

参考

通过条件来控制bean的注册

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值