RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Spring Boot questions
MediumSpring Boot#236 min readJul 11, 2026

What are Conditional Annotations?

practice
learning
Practice modeTest yourself instead of reading straight through

Question: What are conditional annotations in Spring Boot?

Answer: Conditional annotations tell Spring, “Create this bean or configuration only if a certain condition is true.” In Spring Boot, they are a big part of auto-configuration, which means Boot can safely add features only when the right classes, properties, or beans are present. A simple example is loading one service when a property is set, and a fallback service when it is not.

Interview-Ready Answer: I think of conditional annotations as feature gates for Spring beans. They let Spring decide at startup whether a bean should exist based on things like a property value, a class on the classpath, or whether another bean is already present. Spring Boot uses them heavily in auto-configuration so it can add sensible defaults without overriding my app’s own beans. The most common examples are @ConditionalOnProperty, @ConditionalOnClass, and @ConditionalOnMissingBean.

🧠 Memory Map
Memory map — visual summary of this topic

What they are

Detailed Explanation: A conditional annotation is an annotation that works with Spring’s @Conditional mechanism. A Condition is a rule object: if the rule matches, Spring registers the bean or configuration; if it does not, Spring skips it. In Spring Boot, the common versions live under org.springframework.boot.autoconfigure.condition and are designed for auto-configuration.

How it works under the hood

  1. Spring scans your configuration classes and bean methods during application startup.
  2. For each conditional annotation, Spring creates or reuses a Condition rule that checks the environment, the classpath, or the bean registry.
  3. If the rule matches, Spring keeps that bean definition. If it fails, Spring never instantiates that bean.
  4. Because this happens before bean creation, conditional annotations can prevent unnecessary work and avoid startup failures for optional features.
  5. When Boot auto-configuration is involved, it also records the decision in the ConditionEvaluationReport, which is why running with --debug can show you why something was or was not created.

Why and when to use them

  • @ConditionalOnProperty: turn features on or off with config flags such as app.payments.enabled=true.
  • @ConditionalOnClass: only load a bean if a library is present, which is useful for optional integrations.
  • @ConditionalOnMissingBean: provide a safe default only when the user has not defined their own bean.
  • @ConditionalOnBean: create a bean only after another required bean exists.
  • @Profile: choose beans by environment such as dev or prod; this is Spring-wide, not Boot-only, and is less fine-grained than the Boot conditions above.

Comparison with common alternatives

AnnotationBest forCommon gotcha
@ConditionalOnPropertyFeature flagsMissing or unexpected values skip the bean
@ConditionalOnClassOptional librariesThe class must truly be on the classpath
@ConditionalOnMissingBeanSafe defaultsOrder matters in auto-config
@ProfileEnvironment-based groupingToo coarse for feature toggles

Performance and edge cases

These checks are usually very cheap, because Spring Boot tries to evaluate them from metadata instead of eagerly loading classes. The cost is tiny compared with creating the bean itself, so the main benefit is avoiding work. A useful mental model is: conditions are a doorway check, not a full security scan.

Two important edge cases interviewers like:

  • havingValue must match the property value you expect; a typo means the bean disappears.
  • @ConditionalOnMissingBean is great for defaults, but if you use it carelessly you can accidentally hide a user’s custom bean.

Memory Hook: Imagine Spring as a nightclub bouncer: each conditional annotation is a guest list rule. If the name, class, or ticket is right, the bean gets in; otherwise, it waits outside.

Real-World Story: In a checkout service, you may want Stripe support only if the Stripe SDK is present and the flag payments.stripe.enabled=true is set. If the SDK is missing, Spring skips the Stripe bean and the app can still start with another provider or a safe fallback.

What goes wrong when people misunderstand this? A team ships a release with a property typo such as payments.stripe.enbled=true. Spring never creates the Stripe bean, the payment flow falls back unexpectedly, and support sees user complaints like “cards are not being charged through the new provider.” In logs, you might see a missing-bean message or the auto-configuration report showing that the condition did not match. The symptom is often not a crash; it is a feature silently not activating, which makes conditional annotations powerful but also easy to misconfigure.

Spring Boot
package com.example.conditionalannotationsdemo;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@SpringBootApplication
public class ConditionalAnnotationsDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConditionalAnnotationsDemoApplication.class, args);
    }

    // Use ObjectProvider so the demo can report a missing bean cleanly
    // instead of failing startup. In real apps, this is a good way to
    // handle optional dependencies or to show a helpful error message.
    @Bean
    CommandLineRunner runner(ObjectProvider<ShippingService> shippingServiceProvider, Environment environment) {
        return args -> {
            String configuredMode = environment.getProperty("app.shipping", "<not set>");
            ShippingService service = shippingServiceProvider.getIfAvailable();

            System.out.println("app.shipping = " + configuredMode);

            if (service == null) {
                // This is the edge case: a typo or unexpected value means no bean matched.
                System.out.println("No ShippingService bean was created.");
                System.out.println("Use app.shipping=express or leave it unset to get the default service.");
                return;
            }

            System.out.println("Active service: " + service.name());
            System.out.println("Quote for 3 items: " + service.quote(3));
        };
    }

    interface ShippingService {
        String name();

        int quote(int itemCount);
    }

    @Configuration(proxyBeanMethods = false)
    static class ExpressShippingConfiguration {

        @Bean
        @ConditionalOnProperty(prefix = "app", name = "shipping", havingValue = "express")
        ShippingService expressShippingService() {
            return new ShippingService() {
                @Override
                public String name() {
                    return "Express Shipping";
                }

                @Override
                public int quote(int itemCount) {
                    return 25 + (itemCount * 3);
                }
            };
        }
    }

    @Configuration(proxyBeanMethods = false)
    static class StandardShippingConfiguration {

        @Bean
        @ConditionalOnProperty(prefix = "app", name = "shipping", havingValue = "standard", matchIfMissing = true)
        ShippingService standardShippingService() {
            return new ShippingService() {
                @Override
                public String name() {
                    return "Standard Shipping";
                }

                @Override
                public int quote(int itemCount) {
                    return 8 + (itemCount * 1);
                }
            };
        }
    }
}

/*
Run examples:
1) Default behavior: no property set
   -> Standard Shipping is created because matchIfMissing = true.

2) Feature enabled:
   java -jar app.jar --app.shipping=express
   -> Express Shipping is created.

3) Failure path / typo:
   java -jar app.jar --app.shipping=expres
   -> No bean matches, so the runner prints a helpful message.

Why this teaches the concept:
- @ConditionalOnProperty acts like a feature flag.
- matchIfMissing shows the safe default pattern.
- The failure path proves that conditions decide bean creation at startup.
*/

Follow-up & Tricky Questions:

  • How does Spring Boot decide whether a conditional bean should load? It evaluates the condition during startup, before the bean is instantiated. If the rule matches, the definition stays; if not, Spring skips it entirely.
  • Why is @ConditionalOnMissingBean so common in auto-configuration? It lets Boot provide a default implementation without overriding the application’s own bean, which is the core of Boot’s “opinionated but overridable” design.
  • When would you prefer @Profile over conditional annotations? Use @Profile for broad environment splits like dev/test/prod. Use Boot conditionals when the decision depends on a property, a classpath library, or another bean.
  • How do you debug a condition that did not match? Run with --debug or inspect the condition evaluation report. That tells you which condition failed and usually points straight to a wrong property or missing class.
  • Can multiple conditional annotations be combined? Yes. Spring treats them like logical requirements: if one fails, the bean is skipped. That is useful, but it also makes misconfiguration easier if you do not keep the rules simple.
  • Tricky: Does @ConditionalOnProperty create the bean when the property key is misspelled? No. A misspelled key is effectively missing, so the condition will not match unless you explicitly set matchIfMissing = true.
  • Tricky: Is @ConditionalOnClass checking whether the bean exists? No, it checks whether a class is available on the classpath. That is different from bean presence and is used to guard optional dependencies.
  • Tricky: Should I put complex business logic inside @ConditionalOnExpression? Usually no. It is available, but it is harder to read and maintain than a simple property or bean-based condition, and it should not replace normal application logic.

Common Mistakes:

  • Using conditional annotations for business rules. Fix: use them for startup wiring and feature selection, not for per-request decisions.
  • Forgetting that missing or wrong property values skip the bean. Fix: document the exact property name and expected value, and add a fallback or a startup check.
  • Assuming @ConditionalOnMissingBean is always safe. Fix: use it only for true defaults and keep the auto-configuration order in mind.
  • Confusing classpath checks with bean checks. Fix: remember that @ConditionalOnClass asks, “Is the library present?” not “Did Spring create a bean?”

Memory Hook: “If the ticket is valid, Spring lets the bean in.” Think of conditional annotations as a door guard for beans: property, class, or existing-bean checks decide who enters.

Cheat Sheet:

  • Conditional annotations control whether Spring registers a bean or config.
  • Boot uses them heavily for auto-configuration.
  • @ConditionalOnProperty = feature flag.
  • @ConditionalOnClass = optional library.
  • @ConditionalOnMissingBean = safe default.
  • Use --debug to see why a condition matched or failed.

Practice Tasks:

  • Add a second property-based bean, such as pickup, and switch between three implementations.
  • Replace the default bean with @ConditionalOnMissingBean and observe how a user-defined bean overrides it.
  • Start the app with a typo in the property value and confirm the failure path message.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

package com.example.conditionalannotationsdemo; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @SpringBootApplication public class ConditionalAnnotationsDemoApplication { public static void main(String[] args) { SpringApplication.run(ConditionalAnnotationsDemoApplication.class, args); } // Use ObjectProvider so the demo can report a missing bean cleanly // instead of failing startup. In real apps, this is a good way to // handle optional dependencies or to show a helpful error message. @Bean CommandLineRunner runner(ObjectProvider<ShippingService> shippingServiceProvider, Environment environment) { return args -> { String configuredMode = environment.getProperty("app.shipping", "<not set>"); ShippingService service = shippingServiceProvider.getIfAvailable(); System.out.println("app.shipping = " + configuredMode); if (service == null) { // This is the edge case: a typo or unexpected value means no bean matched. System.out.println("No ShippingService bean was created."); System.out.println("Use app.shipping=express or leave it unset to get the default service."); return; } System.out.println("Active service: " + service.name()); System.out.println("Quote for 3 items: " + service.quote(3)); }; } interface ShippingService { String name(); int quote(int itemCount); } @Configuration(proxyBeanMethods = false) static class ExpressShippingConfiguration { @Bean @ConditionalOnProperty(prefix = "app", name = "shipping", havingValue = "express") ShippingService expressShippingService() { return new ShippingService() { @Override public String name() { return "Express Shipping"; } @Override public int quote(int itemCount) { return 25 + (itemCount * 3); } }; } } @Configuration(proxyBeanMethods = false) static class StandardShippingConfiguration { @Bean @ConditionalOnProperty(prefix = "app", name = "shipping", havingValue = "standard", matchIfMissing = true) ShippingService standardShippingService() { return new ShippingService() { @Override public String name() { return "Standard Shipping"; } @Override public int quote(int itemCount) { return 8 + (itemCount * 1); } }; } } } /* Run examples: 1) Default behavior: no property set -> Standard Shipping is created because matchIfMissing = true. 2) Feature enabled: java -jar app.jar --app.shipping=express -> Express Shipping is created. 3) Failure path / typo: java -jar app.jar --app.shipping=expres -> No bean matches, so the runner prints a helpful message. Why this teaches the concept: - @ConditionalOnProperty acts like a feature flag. - matchIfMissing shows the safe default pattern. - The failure path proves that conditions decide bean creation at startup. */