0

springboot的@ConditionalOnBean注解 - 良工说技术

 1 year ago
source link: https://www.cnblogs.com/teach/p/16413802.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

  上篇文章中分析了springboot的自动注入的原理,可在文章后面的推荐阅读中温习哦。在自动注入的原理那篇文章中提到了@ConditionalOnXX注解,今天来看下springboot中的@ConditionalOnXX注解,该注解表示的是一类注解。马上开始吧。

一、@ConditionalOnXX注解初识

  @ConditionalOnXX注解被定义在了spring-boot-autoconfigure包中,有以下几个,

985599-20220626165244433-2067795267.png

  从上图中可以看到经常碰到的@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnMissingClass、@ConditionalOnProperty、@ConditionalOnResource、@ConditionalOnSingleCandidate等。这些注解均在”org.springframework.boot.autoconfigure.condition“包下,感兴趣的小伙伴可以自行查看源码。

二、深入@ConditionalOnBean注解

  上面提到了多个@ConditionalOnXX注解,下面逐一对这些常见的注解进行讲解。有意思的是,这些注解很多都是成对出现的,而且意思都是相近的。今天先来看下@CondtionalOnBean注解

2.1、@ConditionalOnBean

  @ConditionalOnBean注解的定义如下,

985599-20220626171514536-1489248926.png

  可以明确的一点是@ConditionalOnBean可以用在类/方法上。可以配置的属性有下面这些,

985599-20220702102601382-262765950.png

  平时用的比较多的有value、type、name三个,这三个可以看到都是数组,也就是说可以配置多个。

  上面提到@ConditionalOnBean可以配置在方法上,是所有的方法都可以吗?

2.2、@ConditionalOnBean如何标识方法

  @ConditionalOnBean标识在方法上,可以标识在所有的方法上吗,这个我们要从该注解的注释上去找答案了。

985599-20220702103321078-725881021.png

  从上面的注释可以知道,@ConditionalOnBean注解使用在@Bean标识的方法上,都知道@Bean注解的作用是向容器中注入一个bean,也就是@ConditionalOnBean作用的时机是在生成bean的时候。再看注释“the bean class defaults to return type of the factory method”,大体意思是默认返回的bean是工厂方法的类型,这个不好理解,通过一个例子看下。

2.2.1、@ConditionalOnBean(value=)

MyAutoConfig.java

package com.my.template.config;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 配置类
 * @date 2022/7/2 15:02
 */
@Configuration
public class MyAutoConfig {

    @Bean(value = "classA")
    public ClassA classA(){
        return new ClassA();
    }
    @Bean
    @ConditionalOnBean(value = {ClassA.class})
    public ClassB classB(){
        return new ClassB();
    }
}

在上面的配置类中,在@Bean注解的方法上使用了@ConditionalOnBean注解,注解中使用的value属性,代表的是只要存在ClassA这个类边会执行classB()方法,下面看ClassA和ClassB都很简单,就是两个普通的类,

ClassA.java和ClassB.java

package com.my.template.config;
/**
 * @date 2022/7/2 15:03
 */
public class ClassA {
    public ClassA() {
        System.out.println("constructor classA");
    }
}
package com.my.template.config;

/**
 * @date 2022/7/2 15:04
 */
public class ClassB {
    public ClassB() {
        System.out.println("constructor classB");
    }
}

 看下启动日志是否会打印构造方法中的日志,

985599-20220702152043041-532071676.png

可以看到正常打印了,也就是说ClassA和ClassB均被注入到了容器中,这是使用@ConditionalOnBean(value=)的情况,下面看使用@ConditionalOnBean(type=)的情况,

2.2.2、@ConditionalOnBean(type=)

这里的type要求填入的是类的全路径,比如com.my.template.config.ClassA

把配置类中修改为下面的样子,

985599-20220702162022910-1993705779.png

再次启动观察日志,

985599-20220702162113861-1699660843.png

从日志中可以看到依旧是可以的,下面我把MyAutoConfig类中的classA()方法,放到classB()方法下面,

985599-20220702162229993-1172352141.png

再执行看日志,

985599-20220702162305876-1998329903.png

可以看到没有执行ClassB的构造方法,也就是classB()方法没执行。可以得出:在配置类中的@Bean标识的方法是有顺序的,前边的会先解析,后边的后解析,后面的要引用前面的是引用不到的,反之则可以。

这种方式下,没有其他方式可以执行classB()方法吗,有的,使用@ConditionalOnClass(value={ClassA.class}),感兴趣的小伙伴可以自己试试哦。

2.2.3、@ConditionalOnBean(name=)

下面看使用name属性的情况,

MyAutoConfig.java修改成下面的样子,

985599-20220702162858596-488238444.png

启动日志如下,

985599-20220702162941717-566242629.png

正常启动,且初始化了ClassB类。

2.3、ConditionalOnBean标识类

  这里说的标识类,我们一般都默认为标识配置类,即带有@Configuration注解的类。这里同时会有value、type、name三种不同的属性配置,需要注意的是value配置的是Class对象,标识的是只要在解析过程中加载了该类即可。type配置的是全类名,name配置的是bean的名称,type和name的配置要求的是需要解析了该BeanDefinition,同时和顺序是有关系的。演示下面的例子

MyAutoConfig.java

package com.my.template.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自动配置类
 * @date 2022/7/2 15:02
 */
@Configuration
@ConditionalOnBean(type = {"com.my.template.config.ClassA"})
public class MyAutoConfig {
    public MyAutoConfig(){
        System.out.println("constructor MyAutoConfig");
    }
}

MyAutoConfig2.java

package com.my.template.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 自动配置类
 *
 * @author wangcj5
 * @date 2022/7/2 15:02
 */
@Configuration
public class MyAutoConfig2 {
    @Bean
    public ClassA classA(){
        return new ClassA();
    }
    public MyAutoConfig2(){
        System.out.println("constructor MyAutoConfig2");
    }
}

启动日志如下,

985599-20220702164947863-409052089.png

可以看到实例化了MyAutoConfig2,但是MyAutoConfig确没有,这是因为其类上有@ConditionalOnBean(type = {"com.my.template.config.ClassA"})注解,且在解析MyAutoConfig时并未解析ClassA,把该注解换成@ConditionalOnBean(value= {ClassA.class})看看可以吗

985599-20220702165433422-1276380762.png

可以看到还是不行,那么换成@ConditionalOnClass(value={ClassA.class})

985599-20220702165546187-40108929.png

完美解决问题。

  本文主要分析了@ConditionalOnBean注解的使用场景,

  1、该注解的作用时机是在生成bean的时候,确切的说是在解析beanDefinition的时候

  2、该注解可以用在配置类和标识有@Bean的方法上;

  3、三个常用属性value、name、type均有解析顺序的问题;

  4、value、name、type各自的配置方式

本次分享就到这里了,下次,我们@ConditionalOnClass注解见。

深入理解springboot的自动注入

我的第一个springboot  starter

985599-20220702170501565-1624605099.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK