SpringBoot复习:3(@Conditional)

@Conditional

条件装配:满足@Conditional指定的条件时,则进行组件注入

@Conditional注解下面包含许多注解,如下图:
SpringBoot复习:3(@Conditional)_第1张图片
这些注解各有各的功能,这里挑几个来记录一下。

@ConditionalOnBean注解

个人理解:当该注解标注的方法或类有相应的组件时,才注册为组件,否则就不注册为组件

看一下@ConditionalOnBean的源码,可以初步了解它的用法
SpringBoot复习:3(@Conditional)_第2张图片

在方法上使用

测试代码:
配置类
为了测试这里我把配置类中pet01方法的@Bean注解注释掉

@Configuration
public class MyConfig {

    @ConditionalOnBean(name = "cat")//当容器中有名为tom的组件的时候,下面的方法才注册为组件
    @Bean
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }
    
	//@Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

在主程序输出测试结果:

import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;

@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {

    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);

		//判断容器中是否包含user01这个组件
        boolean user = context.containsBean("user01");
        System.out.println("组件中包含user01?"+user);
    }
}

结果:
SpringBoot复习:3(@Conditional)_第3张图片
结果分析:
可以看到,此时容器中并没有user01这个组件了。

在类上使用

测试代码:
配置类:

/**
 * @author:小关同学爱吃汉堡
 * @date: 2021/4/5 17:44
 */
@Configuration
@ConditionalOnBean(name="tom")
public class MyConfig {

    @Bean
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

主程序测试:

import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;

@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {

    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);

		//检查容器中是否有下列组件
        boolean config = context.containsBean("myConfig");
        System.out.println("组件中包含config?"+config);

        boolean user = context.containsBean("user01");
        System.out.println("组件中包含user01?"+user);

        boolean cat = context.containsBean("cat");
        System.out.println("组件中包含cat?"+cat);
    }
}

结果:
SpringBoot复习:3(@Conditional)_第4张图片
我们可以发现,在配置类本身被否决为组件的情况下,配置类下面的所有配置都不生效了

@ConditionalOnMissingBean注解

个人理解:当给定的在bean不存在时,则实例化当前Bean。
再来看一下它的源码,如下图:
SpringBoot复习:3(@Conditional)_第5张图片
@ConditionalOnMissingBean注解作用在@bean定义上,在容器加载它作用的bean时,检查容器中是否存在目标类型(ConditionalOnMissingBean注解里面的值)的bean了,如果存在这跳过原始bean的BeanDefinition加载动作。

在方法上使用

在方法上使用时存在一些坑,让我困惑了好久
当@ConditionalOnMissingBean注解使用name属性时会有一些问题,如下:
配置类

import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author:小关同学爱吃汉堡
 * @date: 2021/4/5 17:44
 */
@Configuration
public class MyConfig {

    @Bean
    //我们在这里使用@ConditionalOnMissingBean注解
    //按理说,当容器中有名为user02的组件的时候,下面的方法就不会注册为组件
    @ConditionalOnMissingBean(name = "user02")
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean
    public User user02(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

在主程序测试:

import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import com.gcl.demo1helloworld.config.MyConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;

@SpringBootApplication(scanBasePackages="com.gcl")
public class Demo1HelloworldApplication {

    public static void main(String[] args) {
        //返回IOC容器
        ConfigurableApplicationContext context = SpringApplication.run(Demo1HelloworldApplication.class, args);

        boolean config = context.containsBean("myConfig");
        System.out.println("组件中包含config?"+config);

        boolean user01 = context.containsBean("user01");
        System.out.println("组件中包含user01?"+user01);

        boolean user02 = context.containsBean("user02");
        System.out.println("组件中包含user02?"+user02);

        boolean cat = context.containsBean("cat");
        System.out.println("组件中包含cat?"+cat);
    }
}

结果:
SpringBoot复习:3(@Conditional)_第6张图片
这结果跟我们想的不太一样啊,user01那里按理说应该是false的,这什么情况???
SpringBoot复习:3(@Conditional)_第7张图片
问过身边的大佬之后,得出一个很草的结论:
这是一个组件创建顺序的问题,因为@ConditionalOnMissingBean(name = “user02”)执行之前,容器里面还没有名叫user02这个组件…所以user01组件总会被创建

正确的使用姿势如下:
配置类:

import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author:小关同学爱吃汉堡
 * @date: 2021/4/5 17:44
 */
@Configuration
public class MyConfig {

	@Bean
	//@ConditionalOnMissingBean(name = "user02")
    //一般来说在这里使用@ConditionalOnMissingBean注解,
    //当容器中有名为user02的组件的时候,下面的方法不会注册为组件
    //但是,这里有一个执行顺序的问题,当执行这条语句的时候,
    //下面的user02组件还未注册,所以下面的方法是一定会被注册为组件的
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean
    @ConditionalOnMissingBean(name = "user01")
    //当user01这个组件存在时,下面的方法不注册为组件
    public User user02(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

主程序代码不变
结果:
SpringBoot复习:3(@Conditional)_第8张图片
这时输出的结果跟我们预期的一致了,user02没有被放到容器里面。

当然,如果我们使用value属性判断时,则不会出现上述的问题,如下:

配置类

import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author:小关同学爱吃汉堡
 * @date: 2021/4/5 17:44
 */
@Configuration
public class MyConfig {

	@Bean
    @ConditionalOnMissingBean(value = User.class)
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean
    public User user02(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

主程序代码不变
结果:
SpringBoot复习:3(@Conditional)_第9张图片
这时候我们发现user01没有被放入到容器中

在类上使用

在类上使用的话,不能使用该类里面注册的组件作为判断条件,不然条件总是为真,如下:
配置类

import ch.qos.logback.core.db.DBHelper;
import com.gcl.demo1helloworld.bean.Pet;
import com.gcl.demo1helloworld.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author:小关同学爱吃汉堡
 * @date: 2021/4/5 17:44
 */
@Configuration
@ConditionalOnMissingBean(name = "cat")//条件装配
public class MyConfig {

    @Bean
    public User user01(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean
    public User user02(){
        User user = new User("张三",19);
        user.setPet(pet01());
        return user;
    }

    @Bean("cat")
    public Pet pet01(){
        return new Pet("cat");
    }
}

主程序代码同上
结果:
SpringBoot复习:3(@Conditional)_第10张图片

你可能感兴趣的:(SpringBoot)