Master Spring Boot 3 Conditional Annotations with Real‑World Examples

This article introduces a continuously updated Spring Boot 3 case collection and PDF e‑book, then dives into the @Conditional annotation, custom condition interfaces, parameterized enhancements, SpringBootCondition usage, and multi‑condition combinations, providing complete code examples and test results.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Boot 3 Conditional Annotations with Real‑World Examples

Spring Boot 3 Practical Case Collection

The collection now contains over 90 practical articles and a PDF e‑book with 94 cases, continuously updated for subscribers, who also receive the source code.

1. Introduction

@Conditional is a powerful annotation introduced in Spring 4.0 that lets developers decide whether to create a bean based on conditions such as environment, properties, classpath, or custom logic. It is usually used together with @Configuration and @Bean , and Spring Boot provides derived annotations like @ConditionalOnProperty , @ConditionalOnBean , @ConditionalOnClass .

2. Practical Cases

2.1 Custom Condition Interface

Define a condition that checks a property pack.api.enabled before registering a bean.

public class EnvCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        return "true".equals(env.getProperty("pack.api.enabled"));
    }
}

Custom annotation:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ApiCondition.class)
public @interface ConditionalOnApi {}

Test controller:

@RestController
@RequestMapping("/api")
@ConditionalOnApi
public class ApiController {
    @PostConstruct
    public void init() {
        System.err.println("ApiController init...");
    }
}

When the property is not set, no output appears; setting pack.api.enabled=true registers the bean.

2.2 Parameterized Enhancement

Make the condition configurable via an annotation attribute.

public @interface ConditionalOnApi {
    String value();
}
public class ApiCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String, Object> attrs = metadata.getAnnotationAttributes(ConditionalOnApi.class.getName());
        String key = (String) attrs.get("value");
        Environment env = context.getEnvironment();
        return "true".equals(env.getProperty(key));
    }
}

Usage:

@ConditionalOnApi("pack.api.enabled")
public class ApiController {}

2.3 Using SpringBootCondition

Extending SpringBootCondition provides logging and richer matching logic.

public class ApiMonitorCondition extends SpringBootCondition {
    private static final ConditionMessage.Builder message = ConditionMessage.forCondition("API Monitor");
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String monitorEnabled = context.getEnvironment().getProperty("pack.api.monitor.enabled");
        if ("true".equals(monitorEnabled)) {
            return ConditionOutcome.match(message.available("开启API监控功能"));
        }
        return ConditionOutcome.noMatch(message.because("API监控功能关闭"));
    }
}

Custom annotation and usage:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(ApiMonitorCondition.class)
public @interface ConditionalOnApiMonitor {}
@Configuration
@ConditionalOnApiMonitor
public class ApiMonitorConfig {}

2.4 Multiple Condition Combination

Combine property, class, and bean existence checks in a single condition.

public class ApiMonitorCondition extends SpringBootCondition {
    private static final ConditionMessage.Builder message = ConditionMessage.forCondition("API Monitor");
    private static final String CLASS_NAME = "com.pack.condition.test.MonitorComponent";
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String monitorEnabled = context.getEnvironment().getProperty("pack.api.monitor.enabled");
        boolean enabled = "true".equals(monitorEnabled);
        boolean isPresent = isPresent(CLASS_NAME, context.getClassLoader());
        if (enabled) {
            if (isPresent) {
                try {
                    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
                    if (beanFactory.containsBean("monitorComponent")) {
                        return ConditionOutcome.match(message.available("开启API监控功能"));
                    }
                    return ConditionOutcome.noMatch(message.because("容器不存在beanName=monitorComponent的Bean对象"));
                } catch (Exception e) {
                    return ConditionOutcome.noMatch(message.because("容器不存在【" + CLASS_NAME + "】类型的Bean"));
                }
            } else {
                return ConditionOutcome.match(message.because("API监控未能开启缺少【" + CLASS_NAME + "】类"));
            }
        } else {
            return ConditionOutcome.noMatch(message.because("API监控功能关闭"));
        }
    }
    private static boolean isPresent(String className, ClassLoader classLoader) {
        try {
            resolve(className, classLoader);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
    private static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
        return classLoader != null ? Class.forName(className, false, classLoader) : Class.forName(className);
    }
}

Testing shows appropriate console messages for each scenario.

... (additional images omitted for brevity) ...

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaspring-bootCustom AnnotationConditional
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.