@Conditional 核心机制

1. 核心接口:Condition

@FunctionalInterface
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
  • matches() 方法返回 true 则条件满足,对应的配置类或 Bean 被创建
  • ConditionContext 提供了 EnvironmentBeanFactoryClassLoader 等上下文
  • AnnotatedTypeMetadata 提供了注解的元数据(如属性值)

2. 标记注解:@Conditional

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

用于标记某个类或方法,只有当指定的 Condition 实现类返回 true 时才生效

实战 1:自定义 @ConditionalOnPortOpen

需求

只有当 Redis 服务(端口 6379)可用时,才加载 RedisService

步骤 1:定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(PortOpenCondition.class)
public @interface ConditionalOnPortOpen {int port();String host() default "localhost";
}

步骤 2:实现 Condition

public class PortOpenCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnPortOpen.class.getName());int port = (Integer) attrs.get("port");String host = (String) attrs.get("host");try (Socket socket = new Socket()) {socket.connect(new InetSocketAddress(host, port), 2000); // 2秒超时return true;} catch (IOException e) {// 端口不通或超时return false;}}
}

步骤 3:使用

@Service
@ConditionalOnPortOpen(port = 6379, host = "redis-server")
public class RedisService {public void setData(String key, String value) { ... }
}

Redis 服务宕机时,RedisService 不会被创建,避免启动报错

 实战 2:自定义“功能开关”注解 @ConditionalOnFeatureEnabled

需求

实现一个通用的功能开关,通过 application.yml 配置启用/禁用。

features:payment: truerecommendation: false

自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(FeatureEnabledCondition.class)
public @interface ConditionalOnFeatureEnabled {String name(); // 如 "payment"
}

Condition 实现

public class FeatureEnabledCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnFeatureEnabled.class.getName());String featureName = (String) attrs.get("name");Environment env = context.getEnvironment();// 读取 features.paymentreturn env.getProperty("features." + featureName, Boolean.class, false);}
}

使用

@Service
@ConditionalOnFeatureEnabled(name = "payment")
public class PaymentService { ... }@Service
@ConditionalOnFeatureEnabled(name = "recommendation")
public class RecommendationService { ... }

无需重启,修改配置即可动态开关功能!