Spring如何根据条件创建bean,@Conditional注解使用方式

 更新时间:2023年06月06日 15:13:02   作者:NameExist  
这篇文章主要介绍了Spring如何根据条件创建bean,@Conditional注解使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Spring根据条件创建bean,@Conditional注解使用

spring提供了一个基于条件创建bean的注解@Conditional。

@Conditional注解定义

//1.可作用于类(接口)、方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    //2.需要传入Condition类型的参数(继承Condition接口)
    Class<? extends Condition>[] value();
}

@Conditional注解使用

1、定义一个类实现Condition接口,返回true则会创建bean,false则不会创建

public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return true;
    }
}

2,定义java配置类

@Configuration
public class BeanConfig {
    @Bean
    @Conditional({MyCondition.class})
    public User user(){
        User user = new User();
        user.setName("小明");
        return user;
    }
}

3、获取bean

@Test
public void test(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
    //获取bean,如果MyCondition返回flase则会报NoSuchBeanDefinitionException,注解注入@Autowired(required = false)
    User u = (User) context.getBean("user");
    System.out.println(u.toString());
}

@Conditional注解常用子注解 

springboot封装conditional注解,在包org.springframework.boot.autoconfigure.condition下。

1、ConditionalOnProperty :当配置文件熟悉值与注解配置的值一致时才会创建bean。prefix配置文件的前缀,name字段名,havingValue字段值

//当配置文件 user.enable=1时才会创建bean,否则不会创建
@ConditionalOnProperty(prefix = "user", name = "enable",havingValue = "1")
@Bean
public User user(){
    User user = new User();
    user.setName("name");
    return user;
}

2、ConditionalOnBean 和 ConditionalOnMissingBean:当某一个bean存在或不存在时才会创建bean。

@Bean
public Dept dept(){
    return new Dept();
}
/**
 * ConditionalOnBean 和 ConditionalOnMissingBean 跟bean的创建顺序有关,
 * 如果先创建了则ConditionalOnBean修饰的bean被创建,后创建传入的bean类型则ConditionalOnMissingBean修饰的bean被创建
 * @return
 */
@ConditionalOnBean(Dept.class)
@Bean
public User user(){
    User user = new User();
    user.setName("name");
    return user;
}
@ConditionalOnMissingBean(Dept.class)
@Bean
public User user1(){
    User user = new User();
    user.setName("name-1");
    return user;
}

3、ConditionalOnExpression:多个条件判断,满足则创建bean,可以用and 和 or。

/**
 * 多个条件判断
 * @return
 */
//@ConditionalOnExpression("'${user.enable}' == '1' and '${user.open}' == '2'")
@ConditionalOnExpression("'${user.enable}' == '1' or '${user.open}' == '2'")
@Bean
public User user(){
    User user = new User();
    user.setName("name");
    return user;
}

按照条件向Spring容器中注册bean

1.@Conditional注解概述

@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。

package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	/**
	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();
}
  • @Conditional注解不仅可以添加到类上,也可以添加到方法上。
  • @Conditional注解中,还存在着一个Condition类型或者其子类型的Class对象数组。
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
	boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

是一个接口,所以在使用@Conditional注解时,需要写一个类来实现Spring提供的Condition接口,它会匹配@Conditional所符合的方法,然后就可以使用在@Conditional注解中定义的类来检查了。

2.向Spring容器注册bean

2.1.不带条件注册bean

package com.tianxia.springannotation.config;
import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {
    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    //通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
    @Scope("prototype")
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
    /**
     * 创建person实例 名为bill
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("bill")
    public Person person01() {
        return new Person("Bill Gates", 62);
    }
    /**
     * 创建person实例 名为linus
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("linus")
    public Person person02() {
        return new Person("linus", 48);
    }
}

测试两个bean是否被注册到Spring容器中

/**
 * 测试bill,linus是否被注入到spring中
 * @author liqb
 * @date 2023-04-23 12:50
 */
@Test
public void test06() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig02.class);
    // 查看IOC容器中Person这种类型的bean都有哪些
    String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
    for (String name : namesForType) {
        System.out.println(name);
    }
}

输出的结果信息如下所示:

结论:说明默认情况下,Spring容器会将单实例并且非懒加载的bean注册到IOC容器中。

2.2.带条件注册bean

需求:比如,如果当前操作系统是Windows操作系统,那么就向Spring容器中注册名称为bill的Person对象;如果当前操作系统是Linux操作系

统,那么就向Spring容器中注册名称为linus的Person对象。要想实现这个需求,我们就得要使用@Conditional注解了。

  • LinuxCondition
package com.tianxia.springannotation.config.configCondition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
 * 判断操作系统是否是Linux系统
 * @author liqb
 * @date 2023-04-23 12:56
 **/
public class LinuxCondition implements Condition{
    /**
     * 匹配方法
     * @author liqb
     * @date 2023-04-23 12:57
     * @param context 判断条件能使用的上下文(环境)
     * @param metadata 当前标注了@Conditional注解的注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 判断操作系统是否是Linux系统
        // 1. 获取到bean的创建工厂(能获取到IOC容器使用到的BeanFactory,它就是创建对象以及进行装配的工厂)
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 2. 获取到类加载器
        ClassLoader classLoader = context.getClassLoader();
        // 3. 获取当前环境信息,它里面就封装了我们这个当前运行时的一些信息,包括环境变量,以及包括虚拟机的一些变量
        Environment environment = context.getEnvironment();
        // 4. 获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();
        // 在这儿还可以做更多的判断,比如说我判断一下Spring容器中是不是包含有某一个bean,就像下面这样,如果Spring容器中果真包含有名称为person的bean,那么就做些什么事情...
        boolean definition = registry.containsBeanDefinition("person");
        String property = environment.getProperty("os.name");
        if (property.contains("linux")) {
            return true;
        }
        return false;
    }
}

理解:通过context的getRegistry()方法获取到的bean定义的注册对象,即BeanDefinitionRegistry对象。

如下所示,可以看到它是一个接口

package org.springframework.beans.factory.support;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.AliasRegistry;
// spring容器中所有的bean都是通过BeanDefinitionRegistry对象来进行注册的
// 因此我们可以通过它来查看Spring容器中注册了哪些bean
public interface BeanDefinitionRegistry extends AliasRegistry {
	// 该方法表明我们可以通过BeanDefinitionRegistry对象向5pring容器中注册一个bean
	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
	throws BeanDefinitionStoreException;
	// 该方法表明我们可以通过BeanDefinitionRegistry对象在Spring容器中移除一个bean
	void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 该方法表明我们可以通过BeanDefinitionRegistry对象查看某个bean的定义信息
	BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
	// 该方法表明我们可以通过BeanDefinitionRegistry对象查看对象Spring
	// 容器中是否包含某一个bean的定义
	boolean containsBeanDefinition(String beanName);
	String[] getBeanDefinitionNames();
	int getBeanDefinitionCount();
	boolean isBeanNameInUse(String beanName);
}

可以通过BeanDefinitionRegistry对象向Spring容器中注册一个bean、移除一个bean、查询某一个bean的定义信息或者判断Spring容器

中是否包含有某一个bean的定义。

  • WindowsCondition
package com.tianxia.springannotation.config.configCondition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
 * 判断操作系统是否是Linux系统
 * @author liqb
 * @date 2023-04-23 14:40
 **/
public class WindowsCondition implements Condition {
    /**
     * 匹配方法
     * @author liqb
     * @date 2023-04-23 12:57
     * @param context 判断条件能使用的上下文(环境)
     * @param metadata 当前标注了@Conditional注解的注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("Windows")) {
            return true;
        }
        return false;
    }
}

配置类中使用@Conditional注解添加条件,添加该注解后的方法如下所示。

package com.tianxia.springannotation.config;
import com.tianxia.springannotation.config.configCondition.LinuxCondition;
import com.tianxia.springannotation.config.configCondition.WindowsCondition;
import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
public class MainConfig02 {
    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    //通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
    @Scope("prototype")
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
    /**
     * 创建person实例 名为bill
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("bill")
    @Conditional({WindowsCondition.class})
    public Person person01() {
        return new Person("Bill Gates", 62);
    }
    /**
     * 创建person实例 名为linus
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("linus")
    @Conditional({LinuxCondition.class})
    public Person person02() {
        return new Person("linus", 48);
    }
}

在运行测试方法,输出的结果信息如下所示:

输出结果中不再含有名称为linus的bean了,这说明程序中检测到当前操作系统为Windows 10之后,没有向Spring容器中注册名称为linus的bean。

@Conditional注解也可以标注在类上,标注在类上的含义是:只有满足了当前条件,这个配置类中配置的所有bean注册才能生效,也就是对配置类中的组件进行统一设置。

package com.tianxia.springannotation.config;
import com.tianxia.springannotation.config.configCondition.LinuxCondition;
import com.tianxia.springannotation.config.configCondition.WindowsCondition;
import com.tianxia.springannotation.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
 * 配置类
 * @author liqb
 * @date 2023-04-23 9:45
 **/
@Configuration
// 满足当前条件,这个类中配置的所有bean注册才能生效
@Conditional({LinuxCondition.class}) 
public class MainConfig02 {
    /**
     * 创建person实例
     * @author liqb
     * @date 2023-04-23 09:46
     * @return
     */
    @Bean("person02")
    //通过@Scope注解来指定该bean的作用范围,也可以说成是调整作用域
    @Scope("prototype")
    public Person person() {
        System.out.println("给容器中添加咱们这个Person对象...");
        return new Person("liqb", 24);
    }
    /**
     * 创建person实例 名为bill
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("bill")
    @Conditional({WindowsCondition.class})
    public Person person01() {
        return new Person("Bill Gates", 62);
    }
    /**
     * 创建person实例 名为linus
     * @author liqb
     * @date 2023-04-23 12:47
     * @return
     */
    @Bean("linus")
    @Conditional({LinuxCondition.class})
    public Person person02() {
        return new Person("linus", 48);
    }
}

运行测试方法之后,发现输出的结果信息如下所示:

没有任何bean的定义信息输出,这是因为程序检测到了当前操作系统为window,没有向Spring容器中注册任何bean的缘故导致的。

3.@Conditional的扩展注解

@Conditional扩展注解作用 (判断是否满足当前指定条件)
@ConditionOnJava系统的Java版本是否符合要求
@ConditionOnBean容器中存在指定Bean
@ConditionOnMissingBean容器中不存在指定Bean
@ConditionOnExpression满足SpEL表达式指定
@ConditionOnClass系统中有指定的类
@ConditionOnMissingClass系统中没有指定的类
@ConditionOnSingleCandidate容器中只有一个指定的bean,或者这个bean是首选bean
@ConditionOnProperty系统中指定的属性是否有指定的值
@ConditionOnResource类路径下是否存在指定资源文件
@ConditionalOnWebApplication当前是web环境
@ConditionalOnNoWebApplication当前不是web环境
@ConditionalOnJndiJNDI存在指定项

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 浅谈SpringMVC+Spring3+Hibernate4开发环境搭建

    浅谈SpringMVC+Spring3+Hibernate4开发环境搭建

    MVC已经是现代Web开发中的一个很重要的部分,本文介绍一下SpringMVC+Spring3+Hibernate4的开发环境搭建,有兴趣的可以了解一下。
    2017-01-01
  • IDEA集成Gitee码云的实现步骤

    IDEA集成Gitee码云的实现步骤

    本文主要介绍了IDEA集成Gitee码云的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • java使用POI操作excel文件

    java使用POI操作excel文件

    本文主要介绍了java使用POI操作excel文件,实现批量导出和导入的方法。具有很好的参考价值。下面跟着小编一起来看下吧
    2017-03-03
  • Java实现的求逆矩阵算法示例

    Java实现的求逆矩阵算法示例

    这篇文章主要介绍了Java实现的求逆矩阵算法,涉及java基于数组的矩阵遍历与运算相关操作技巧,需要的朋友可以参考下
    2017-09-09
  • Python实现filter函数实现字符串切分

    Python实现filter函数实现字符串切分

    这篇文章主要介绍了Python实现filter函数实现字符串切分,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • idea查看properties中文变成unicode码的解决方案

    idea查看properties中文变成unicode码的解决方案

    这篇文章主要介绍了idea查看properties中文变成unicode码的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Java反射机制(Reflection)浅析

    Java反射机制(Reflection)浅析

    这篇文章主要介绍了Java反射机制(Reflection)浅析,本文以实例讲解Java的反射机制,需要的朋友可以参考下
    2014-07-07
  • 两个List集合取相同重复数据的方法

    两个List集合取相同重复数据的方法

    今天小编就为大家分享一篇关于两个List集合取相同重复数据的方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • 浅谈Java 并发的底层实现

    浅谈Java 并发的底层实现

    这篇文章主要介绍了浅谈Java 并发的底层实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 自从在 IDEA 中用了热部署神器 JRebel 之后,开发效率提升了 10(真棒)

    自从在 IDEA 中用了热部署神器 JRebel 之后,开发效率提升了 10(真棒)

    在javaweb开发过程中,使用热部署神器 JRebel可以使class类还是更新spring配置文件都能立马见到效率,本文给大家介绍JRebel的两种安装方法,小编建议使用第二种方法,具体安装步骤跟随小编一起看看吧
    2021-06-06

最新评论