Spring 条件组件注解:@Conditional
与 @ConditionalOnBean
文章目录
- Spring 条件组件注解:`@Conditional` 与 `@ConditionalOnBean`
- 一、`@Conditional` 基本使用
- 0、条件组件是在非条件组件注册之后再进行注册的
- 1、概述
- 2、代码演示
- 3、`@Conditional` 的优缺点
- 二、Condition 接口的 `ConditionContext` 参数
- 1、Condition 接口
- 2、`ConditionContext` 参数
- 源码
- 方法简介
- 使用示例
- Condition实现需要遵循的规则
- 三、`@ConditionalOnBean` 条件注解
- 1、概述
- 简介
- 两个属性
- 2、代码示例
- 两个 Bean
- 使用 `@ConditionalOnBean` 注解
一、@Conditional
基本使用
0、条件组件是在非条件组件注册之后再进行注册的
条件组件是在其他非条件组件注册之后注册的。
在Spring Framework中,条件组件通常会与非条件组件一起使用,以实现更复杂的配置。当一个条件组件被激活时,它会根据其定义的条件来决定是否加载其依赖的其他组件。
通常情况下,条件组件会在Spring容器启动时被注册,但它们并不会立即执行它们的逻辑。相反,当Spring容器检测到满足条件的情况时,条件组件才会被激活并执行它们的逻辑。这意味着如果一个条件组件依赖于另一个非条件组件,那么只有在满足条件时,后者才会被加载和执行。
因此,条件组件通常是在其他非条件组件之后注册的,并且只有在满足特定条件时才会被激活和执行。这种设计可以确保条件组件只在需要它们的时候才被加载和执行,从而提高应用程序的性能和效率。
1、概述
@Conditional注解用于根据某些条件来决定是否组件(比如Bean)是否生效。可用于:
-
@Bean
-
@Component
-
@Configuration
等来条件化这些组件的注册。
@Conditional 接受一个实现 Condition 接口的类作为参数。如果给定的条件计算结果为 true ,则相应的组件将被注册。
2、代码演示
定义一个 OnProd 条件来在生产环境中激活 Bean
public class OnProd implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return "prod".equals(context.getEnvironment().getProperty("env"));}
}
然后,可以在@Bean、@Configuration等上使用该条件
@Configuration
@Conditional(OnProd.class)
public class ProdConfig {@Beanpublic 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();@NullableConfigurableListableBeanFactory getBeanFactory();Environment getEnvironment();ResourceLoader getResourceLoader();@NullableClassLoader getClassLoader();}
方法简介
getBeanFactory()
:获取BeanFactory
,可以检查其是否包含某个BeangetEnvironment()
:获取Environment
,可访问环境变量、应用程序参数等getResourceLoader()
:获取ResourceLoader
,可获取类路径资源getClassLoader()
:获取ClassLoader
getRegistry()
:获取Bean定义注册表,可检查是否注册了某个Bean
使用示例
该条件通过获取Environment并检查"db.type"属性来判断是否匹配。
public class MySQLCondition implements Condition {@Overridepublic 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 { @Overridepublic 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 { @Beanpublic Baz baz() { ... }
}@Configuration
@ConditionalOnBean(type = Bar.class) // 仅在Bar类型Bean存在时激活
public class BarConfig {@Beanpublic Qux qux() { ... }
}