聊聊 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 配置类的类级别或者方法级别,然后根据每个注解的作用传参就行。

原文地址:https://www.cnblogs.com/itzhouq/p/conditional.html