SpringBoot基础篇Bean之条件注入@Condition使用姿势

简介: 前面几篇关于Bean的基础博文中,主要集中在Bean的定义和使用,但实际的情况中有没有一些场景是不加载我定义的bean,或者只有满足某些前提条件的时候才加载我定义的Bean呢?本篇博文将主要介绍bean的加载中,条件注解@Conditional的相关使用

前面几篇关于Bean的基础博文中,主要集中在Bean的定义和使用,但实际的情况中有没有一些场景是不加载我定义的bean,或者只有满足某些前提条件的时候才加载我定义的Bean呢?


本篇博文将主要介绍bean的加载中,条件注解@Conditional的相关使用


I. @Conditional注解



这个注解在Spring4中引入,其主要作用就是判断条件是否满足,从而决定是否初始化并向容器注册Bean


1. 定义


@Conditional注解定义如下,其内部主要就是利用了Condition接口,来判断是否满足条件,从而决定是否需要加载Bean


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


下面是Condtion接口的定义,这个可以说是最基础的入口了,其他的所有条件注解,归根结底,都是通过实现这个接口进行扩展的

@FunctionalInterface
public interface Condition {
    boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
复制代码


这个接口中,有个参数比较有意思ConditionContext,它持有不少有用的对象,可以用来获取很多系统相关的信息,来丰富条件判断,接口定义如下

public interface ConditionContext {
    // 获取Bean定义
    BeanDefinitionRegistry getRegistry();
    // 获取Bean工程,因此就可以获取容器中的所有bean
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();
    // environment 持有所有的配置信息
    Environment getEnvironment();
    // 资源信息
    ResourceLoader getResourceLoader();
    // 类加载信息
    @Nullable
    ClassLoader getClassLoader();
}
复制代码


2. 使用说明


通过一个小例子,简单的说一下如何使用Condition和@Conditional注解,来实现bean的条件加载


首先我们定义一个随机产生数据的类,其功能就是随机生成一些数据


public class RandDataComponent<T> {
    private Supplier<T> rand;
    public RandDataComponent(Supplier<T> rand) {
        this.rand = rand;
    }
    public T rand() {
        return rand.get();
    }
}
复制代码


我们目前提供两种随机数据生成的bean,但是需要根据配置来选择具体选中的方式,因此我们如下定义Bean


@Configuration
public class ConditionalAutoConfig {
    @Bean
    @Conditional(RandIntCondition.class)
    public RandDataComponent<Integer> randIntComponent() {
        return new RandDataComponent<>(() -> {
            Random random = new Random();
            return random.nextInt(1024);
        });
    }
    @Bean
    @Conditional(RandBooleanCondition.class)
    public RandDataComponent<Boolean> randBooleanComponent() {
        return new RandDataComponent<>(() -> {
            Random random = new Random();
            return random.nextBoolean();
        });
    }
}
复制代码


上面的配置,先不管@Conditional注解的内容,单看两个Bean的定义,一个是定义int随机数生成;一个是定义boolean随机生成;


但是我们的系统中,只需要一个随机数据生成器即可,我们选择根据配置

conditional.rand.type的值来选择到底用哪个,配置如下


# int 表示选择随机产生int数据; 非int 表示随机产生boolean数据
conditional.rand.type=int
复制代码


接下来就得看这个条件如何加上了,也就是上面配置类ConditionalAutoConfig中两个注解的内容了,两个类都是实现Condition的接口,具体如下


public class RandBooleanCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
        return "boolean".equalsIgnoreCase(type);
    }
}
public class RandIntCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String type = conditionContext.getEnvironment().getProperty("conditional.rand.type");
        return "int".equalsIgnoreCase(type);
    }
}
复制代码


上面的实现也比较清晰,获取配置值,然后判断,并返回true/fase;返回true,则表示这个条件满足,那么这个Bean就可以被加载了;否则这个Bean就不会创建


3. 测试与验证


针对上面的配置与实现,写一个测试类如下


@RestController
@RequestMapping(path = "/conditional")
public class ConditionalRest {
    @Autowired
    private RandDataComponent randDataComponent;
    @GetMapping(path = "/show")
    public String show() {
        String type = environment.getProperty("conditional.rand.type");
        return randDataComponent.rand() + " >>> " + type;
    }
}
复制代码


当配置文件的值为int时,每次访问返回的应该都是正整数,演示如下图


image.png

将配置的值改成boolean之后,再次测试如下图

image.png


II. 扩展与小结



上面的测试演示了通过配置文件选择注入Bean的情况,如果一个Bean是通过自动扫描加载的,是否可以直接在Bean的类上添加注解来决定是否载入呢?


1. 自动扫描Bean的条件加载


从使用来讲,和前面的没有什么区别,只是将注解放在具体的类上而言,同样给出一个示例,先定义一个bean


@Component
@Conditional(ScanDemoCondition.class)
public class ScanDemoBean {
    @Value("${conditional.demo.load}")
    private boolean load;
    public boolean getLoad() {
        return load;
    }
}
复制代码


对应的判断条件如下,当配置文件中conditional.demo.load为true时,才会加载这个配置,否则不实例化

public class ScanDemoCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return "true".equalsIgnoreCase(conditionContext.getEnvironment().getProperty("conditional.demo.load"));
    }
}
复制代码


测试类和前面差不多,稍微注意下的就是自动注入时,改一下必要条件,避免bean不存在时报错

@Autowired(required = false)
private ScanDemoBean scanDemoBean;
@GetMapping(path = "/scan")
public String showDemo() {
    String type = environment.getProperty("conditional.demo.load");
    if (scanDemoBean == null) {
        return "not exists! >>>" + type;
    } else {
        return "load : " + scanDemoBean.getLoad() + " >>>" + type;
    }
}
复制代码


当配置为true时,bean应该存在,走上面的else逻辑

image.png

当配置为false时,不会加载bean,走if逻辑

image.png


2. 小结


通过@Conditional注解配合Condition接口,来决定给一个bean是否创建和注册到Spring容器中,从而实现有选择的加载bean


a. 优势


这样做的目的是什么呢?


  • 当有多个同名bean时,怎么抉择的问题
  • 解决某些bean的创建有其他依赖条件的case


b. 更多注解


上面可以控制bean的创建,但通过上面的流程,会发现有一点繁琐,有没有什么方式可以简化上面的流程呢?


只用一个注解就好,不要自己再来实现Condtion接口,Spring框架提供了一系列相关的注解,如下表


注解 说明
@ConditionalOnSingleCandidate 当给定类型的bean存在并且指定为Primary的给定类型存在时,返回true
@ConditionalOnMissingBean 当给定的类型、类名、注解、昵称在beanFactory中不存在时返回true.各类型间是or的关系
@ConditionalOnBean 与上面相反,要求bean存在
@ConditionalOnMissingClass 当给定的类名在类路径上不存在时返回true,各类型间是and的关系
@ConditionalOnClass 与上面相反,要求类存在
@ConditionalOnCloudPlatform 当所配置的CloudPlatform为激活时返回true
@ConditionalOnExpression spel表达式执行为true
@ConditionalOnJava 运行时的java版本号是否包含给定的版本号.如果包含,返回匹配,否则,返回不匹配
@ConditionalOnProperty 要求配置属性匹配条件
@ConditionalOnJndi 给定的jndi的Location 必须存在一个.否则,返回不匹配
@ConditionalOnNotWebApplication web环境不存在时
@ConditionalOnWebApplication web环境存在时
@ConditionalOnResource 要求制定的资源存在



相关文章
|
10天前
|
Java
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
SpringBoot构建Bean(RedisConfig + RestTemplateConfig)
30 2
|
30天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
1月前
|
Java Spring 容器
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
这篇文章讨论了在Spring Boot 3.2.1版本中,同名同类型的bean和@Service注解类之间冲突的问题得到了解决,之前版本中同名bean会相互覆盖,但不会在启动时报错,而在配置文件中设置`spring.main.allow-bean-definition-overriding=true`可以解决这个问题。
83 0
Springboot3.2.1搞定了类Service和bean注解同名同类型问题修复
|
2月前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
1月前
|
NoSQL Java 数据库连接
springBoot:整合其他框架&condition&切换web配置 (五)
本文档介绍了如何在Spring Boot项目中整合JUnit、Redis和MyBatis等框架,并提供了相应的依赖配置示例。同时,还展示了如何通过条件注解实现Bean的条件创建,以及如何切换Web服务器配置,从默认的Tomcat切换到Jetty。
|
1月前
|
Java Shell C++
Springboot加载注入bean的方式
本文详细介绍了Spring Boot中Bean的装配方法。首先讲解了使用@Component、@Service、@Controller、@Repository等注解声明Bean的方式,并解释了这些注解之间的关系及各自适用的层次。接着介绍了通过@Configuration和@Bean注解定义Bean的方法,展示了其灵活性和定制能力。最后讨论了@Component与@Bean的区别,并提供了在Spring Boot应用中装配依赖包中Bean的三种方法:使用@ComponentScan注解扫描指定包、使用@Import注解导入特定Bean以及在spring.factories文件中配置Bean。
|
4月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
103 11
|
3月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
43 0
|
3月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
134 0
|
3月前
|
Java Spring 容器
Java SpringBoot 中,动态执行 bean 对象中的方法
Java SpringBoot 中,动态执行 bean 对象中的方法
40 0