Spring 条件组件注解:`@Conditional` 与 `@ConditionalOnBean`

Spring 条件组件注解:@Conditional@ConditionalOnBean

一、@Conditional 基本使用

0、条件组件是在非条件组件注册之后再进行注册的

条件组件是在其他非条件组件注册之后注册的。

在Spring Framework中,条件组件通常会与非条件组件一起使用,以实现更复杂的配置。当一个条件组件被激活时,它会根据其定义的条件来决定是否加载其依赖的其他组件。

通常情况下,条件组件会在Spring容器启动时被注册,但它们并不会立即执行它们的逻辑。相反,当Spring容器检测到满足条件的情况时,条件组件才会被激活并执行它们的逻辑。这意味着如果一个条件组件依赖于另一个非条件组件,那么只有在满足条件时,后者才会被加载和执行。

因此,条件组件通常是在其他非条件组件之后注册的,并且只有在满足特定条件时才会被激活和执行。这种设计可以确保条件组件只在需要它们的时候才被加载和执行,从而提高应用程序的性能和效率。

1、概述

@Conditional注解用于根据某些条件来决定是否组件(比如Bean)是否生效。可用于:

  • @Bean

  • @Component

  • @Configuration

等来条件化这些组件的注册。

@Conditional 接受一个实现 Condition 接口的类作为参数。如果给定的条件计算结果为 true ,则相应的组件将被注册。

2、代码演示

定义一个 OnProd 条件来在生产环境中激活 Bean

public class OnProd implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return "prod".equals(context.getEnvironment().getProperty("env"));
    }
}

然后,可以在@Bean、@Configuration等上使用该条件

@Configuration
@Conditional(OnProd.class) 
public class ProdConfig {
    @Bean
    public Foo foo() { ... }
}

3、@Conditional 的优缺点

我们也可以定义更复杂的条件,检查网络接口、系统属性等等。所以,@Conditional注解为我们带来了如下好处:

  • 根据环境、属性等条件化地激活Bean;
  • 避免在不同环境中重复定义Bean;
  • 引入更高的灵活性和可扩展性;

但是,过度使用@Conditional也会带来一定的缺点:

  • 阅读和理解配置变得更加复杂
  • debugging变得更加困难;
  • 激活/不激活Bean的根本原因可能很难确定 ;

二、Condition 接口的 ConditionContext 参数

1、Condition 接口

用于注册组件必须匹配的单个条件。

在要注册bean定义之前立即检查条件,并可以根据此时可以确定的任何标准自由否决注册。

条件必须遵循与 BeanFactoryPostProcessor 相同的限制,并注意不要与bean实例交互。为了更细粒度地控制与 @ Configuration bean交互的条件,可以考虑实现 ConfigurationCondition 接口。

2、ConditionContext 参数

源码

package org.springframework.context.annotation;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;

/**
 * Context information for use by {@link Condition} implementations.
 * 上下文信息,供 Condition 实现使用。
 *
 * @author Phillip Webb
 * @author Juergen Hoeller
 * @since 4.0
 */
public interface ConditionContext {
	BeanDefinitionRegistry getRegistry();
	@Nullable
	ConfigurableListableBeanFactory getBeanFactory();
	Environment getEnvironment();
	ResourceLoader getResourceLoader();
	@Nullable
	ClassLoader getClassLoader();

}

方法简介

  • getBeanFactory():获取 BeanFactory ,可以检查其是否包含某个Bean
  • getEnvironment():获取 Environment ,可访问环境变量、应用程序参数等
  • getResourceLoader():获取 ResourceLoader ,可获取类路径资源
  • getClassLoader():获取 ClassLoader
  • getRegistry():获取Bean定义注册表,可检查是否注册了某个Bean

使用示例

该条件通过获取Environment并检查"db.type"属性来判断是否匹配。

public class MySQLCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, 
                            AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        return "MySQL".equals(env.getProperty("db.type")); 
    }
}

检查是否注册了名为"foo"且类型为 Foo.class 的Bean。

public class FooExistsCondition implements Condition { 
    @Override
    public boolean matches(ConditionContext context, 
                            AnnotatedTypeMetadata metadata) {
        BeanFactory bf = context.getBeanFactory();
        return bf.containsBean("foo") && bf.isTypeMatch("foo", Foo.class); 
    } 
}

Condition实现需要遵循的规则

  • 不要在构造方法或matches方法中触发Bean定义的解析(会导致无限递归);
  • 缓存昂贵的操作结果(如类型匹配检查)以提高性能;
  • 尽量保持 matches 方法是幂等的(多次调用得到同样结果);

三、@ConditionalOnBean 条件注解

1、概述

简介

  • 仅在给定 Bean 存在的条件下,才会实例化配置类或Bean。

  • 可以实现依赖其他 Bean 的条件化配置。

当指定的 Bean 在 BeanFactory 中存在且类型匹配时,条件满足,相关的配置类或Bean会被实例化。

两个属性

  • value:指定需要检查的Bean名称(或名称数组)。

  • type:指定需要检查的Bean类型(或类型数组),不是必须的。

2、代码示例

两个 Bean

@Component
public class Foo { ... } 

@Component 
public class Bar { ... }

使用 @ConditionalOnBean 注解

  • FooConfig 会仅在名称为"foo"的Bean存在时被激活

  • BarConfig 会仅在类型为Bar的Bean存在时被激活

@Configuration
@ConditionalOnBean("foo") // 仅在foo Bean存在时激活
public class FooConfig { 
    @Bean
    public Baz baz() { ... }
}

@Configuration
@ConditionalOnBean(type = Bar.class) // 仅在Bar类型Bean存在时激活
public class BarConfig {
    @Bean
    public Qux qux() { ... }  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值