聊聊 Spring Boot 中的 @Conditional 注解
阅读 SpringBoot 自动配置源码的时候,可以看到很多以 @Conditional 开头的注解,
这类注解的作用是根据条件决定是否注册 bean。本文对不同的条件做个归纳总结。
了解 @Conditional
@Conditional 注解是 Spring 4.0 中新增核心注解,作用是提供自动装配的条件约束,一般与 @Configuration 和 @Bean 配合使用。简单说,Spring 在解析 @Configuration 配置类的时候,如果该配置类增加了 @Conditional 注解,那么会根据该注解配置的条件来决定是否要实现 Bean 的装配。
@Conditonal 注解类声明代码如下,该注解可以接收一个Condition 的数组,位于 org.springframework.context.annotation
包下。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
Class<? extends Condition>[] value();
}
Condition 是一个函数式接口,提供了 matches 方法,它主要是提供一个条件匹配规则,返回 true 表示可以注入 Bean,反之不注入。
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
条件装配案例
下面基于 @Conditional 实现一个条件装配案例。
自定义一个 Condition ,如果当前操作系统是 Linux 就注册 linux,如果当前操作系统是 Mac OS X 就注册 mac。
- 首先创建两个空类:
public class Linux {}
public class MacOSX {}
- 创建 Condition :分别匹配两种操作系统
package cn.itzhouq.starter.test.config;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 获取系统环境的属性
String systemName = conditionContext.getEnvironment().getProperty("os.name");
if (systemName.contains("Linux")) {
return true;
}
return false;
}
}
package cn.itzhouq.starter.test.config;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MacOSXCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String systemName = conditionContext.getEnvironment().getProperty("os.name");
if (systemName.contains("Mac OS X")) {
return true;
}
return false;
}
}
- 配置类
package cn.itzhouq.starter.test.config;
import cn.itzhouq.starter.test.pojo.Linux;
import cn.itzhouq.starter.test.pojo.MacOSX;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Conditional(LinuxCondition.class)
@Bean
public Linux linux() {
return new Linux();
}
@Conditional(MacOSXCondition.class)
@Bean
public MacOSX mac() {
return new MacOSX();
}
}
- 测试类
package cn.itzhouq.starter.test;
import cn.itzhouq.starter.test.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author zhouquan
* @date 2020/10/7 15:31
*/
public class ConditionTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println("name = " + name);
}
}
}
- 结果:
name = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
name = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
name = org.springframework.context.annotation.internalCommonAnnotationProcessor
name = org.springframework.context.event.internalEventListenerProcessor
name = org.springframework.context.event.internalEventListenerFactory
name = appConfig
name = mac
上面是我在 Mac 上运行程序的结果。如果修改参数:
运行结果会显示 linux。
Spring Boot 中的 @Conditional
通过上面的案例可以明白 @Conditional 是如何帮助实现 Bean 的条件注入的。在 Spring Boot 中,针对该注解做了扩展,提供了更简单的使用形式。扩展的注解主要有以下这些:
- ConditionalOnBean/ConditionalOnMissingBean:容器中存在某个 Bean 或者不存在某个 Bean 时进行装载。
- ConditionalOnClass/ConditionalOnMissingClass:classpath 下存在指定的类或者不存在指定的类时进行装载。
- ConditionalOnCloudPlatform:只有运行在指定的云平台上才加载指定的 Bean。
- ConditionalOnExpression:基于 SpEl 表达式的条件判断。
- ConditionalOnJava:只有运行指定版本的 Java 才加载 Bean。
- ConditionalOnJndi:只有指定资源通过 JNDI 加载后才加载 Bean。
- ConditionalOnWebApplication/ConditionalOnNotWebApplication:如果是 Web 应用或者不是 Web 应用,才加载指定的 Bean。
- ConditionalOnProperty:系统中指定的属性是否有对应的值。
- ConditionalOnResource:要加载的 Bean 依赖指定新资源是否存在于 classpath 中。
- ConditionalOnSingleCandidate:只有在确定了给定 Bean 类的单个选项时才会加载 Bean。
这些注解只需要添加到 @Configuration 配置类的类级别或者方法级别,然后根据每个注解的作用传参就行。
转载请注明作者和出处。