Back to Bean Types & Stereotypes
Q2Question 2 of 2

What are @Conditional and @SpringBootApplication?

Key Concepts
  • @Conditional registers a bean only when a runtime condition is met
  • @ConditionalOnClass activates auto-configuration when a library is on the classpath
  • @ConditionalOnMissingBean backs off so your custom bean always wins over auto-config
  • @ConditionalOnProperty enables feature flags without changing code
  • @SpringBootApplication combines @Configuration, @EnableAutoConfiguration, and @ComponentScan
  • Spring Framework is the core IoC and DI container, Spring MVC is the web layer on the Servlet API, and Spring Boot adds opinionated auto-configuration with an embedded server

Definition

@Conditional registers beans only when a runtime condition is met, and @SpringBootApplication combines @Configuration, @EnableAutoConfiguration, and @ComponentScan into a single annotation.
  • @Conditional – registers a bean only when a specific condition evaluates to true at startup.
  • @ConditionalOnClass – activates a configuration when a given class exists on the classpath, which is how starter dependencies trigger auto-configuration.
  • @ConditionalOnMissingBean – backs off when the developer has already defined a bean of that type, so custom configuration always wins.
  • @ConditionalOnProperty – creates a bean only when a specific property is set to a specific value, enabling feature flags.
  • @SpringBootApplication – a convenience annotation that combines @Configuration (marks the class as a bean source), @EnableAutoConfiguration (triggers classpath-based auto-configuration), and @ComponentScan (discovers annotated components in the package and its sub-packages).

Think of a bank branch opening for the day. The branch manager checks conditions before assigning roles: hire a vault guard only if the branch has a vault (@ConditionalOnClass), use the default cash counter only if no one has already set up a custom one (@ConditionalOnMissingBean), and enable the foreign exchange desk only if the regional memo says forex is enabled (@ConditionalOnProperty). @SpringBootApplication is the grand opening checklist that registers the branch, auto-sets up equipment, and recruits staff in one step.

Understanding the Concepts

What is @Conditional in Spring?

@Conditional is a Spring Framework annotation that tells the container to register a bean only when a specific condition evaluates to true at startup. The condition is a class that implements the Condition interface and overrides the matches method. If matches returns true, the bean is registered. If it returns false, Spring skips the bean entirely.

This is the foundation of Spring Boot's entire auto-configuration system. Every auto-configuration class uses one or more conditional annotations to decide whether its beans should be created.

JAVA
package com.bank.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class VaultAvailableCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String vaultEnabled = context.getEnvironment().getProperty("bank.vault.enabled");
        return "true".equalsIgnoreCase(vaultEnabled);
    }
}
JAVA
package com.bank.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class VaultConfig {

    @Bean
    @Conditional(VaultAvailableCondition.class)
    public VaultGuardService vaultGuardService() {
        return new VaultGuardService();
    }
}
TEXT
If bank.vault.enabled=true  -> VaultGuardService bean is created
If bank.vault.enabled=false -> VaultGuardService bean is skipped entirely

What are the @Conditional variants in Spring Boot?

Spring Boot provides several built-in conditional annotations so you do not need to write custom Condition classes for common scenarios.

| Annotation | Condition | Typical Use |
|---|---|---|
| @ConditionalOnClass | Class exists on classpath | Starter auto-configuration |
| @ConditionalOnMissingBean | No bean of that type exists | Back off for custom beans |
| @ConditionalOnProperty | Property has a specific value | Feature flags |
| @ConditionalOnBean | A bean of that type already exists | Dependent configuration |
| @ConditionalOnMissingClass | Class is not on classpath | Fallback configuration |
| @ConditionalOnWebApplication | Running in a web context | Web-specific beans |
| @ConditionalOnNotWebApplication | Not a web context | CLI or batch beans |

@ConditionalOnClass is the most important one. When you add spring-boot-starter-data-redis to your pom.xml, the Redis classes appear on the classpath. Spring Boot's RedisAutoConfiguration is annotated with @ConditionalOnClass(RedisConnectionFactory.class), so it activates automatically. Remove the starter and the Redis beans disappear.

JAVA
package com.bank.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigDecimal;

@Configuration
public class InterestAutoConfiguration {

    // Activates only when Redis is on the classpath
    @Bean
    @ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
    public RedisCacheService redisCacheService() {
        return new RedisCacheService();
    }

    // Activates only when NO cache service bean exists (fallback)
    @Bean
    @ConditionalOnMissingBean(CacheService.class)
    public InMemoryCacheService inMemoryCacheService() {
        return new InMemoryCacheService();
    }

    // Activates only when the fraud feature flag is true
    @Bean
    @ConditionalOnProperty(name = "bank.fraud.enabled", havingValue = "true")
    public FraudCheckService fraudCheckService() {
        return new FraudCheckService();
    }
}
TEXT
With Redis on classpath:
  -> RedisCacheService created
  -> InMemoryCacheService skipped (ConditionalOnMissingBean fails)

Without Redis on classpath:
  -> RedisCacheService skipped (ConditionalOnClass fails)
  -> InMemoryCacheService created (no CacheService bean exists)

With bank.fraud.enabled=true:
  -> FraudCheckService created

With bank.fraud.enabled=false or missing:
  -> FraudCheckService skipped

Why does @ConditionalOnMissingBean matter?

@ConditionalOnMissingBean is the reason your custom configuration always wins over Spring Boot's auto-configuration. Auto-configuration classes use this annotation on their default beans. If you define your own bean of the same type, Spring Boot detects it and backs off.

Rajesh, the developer at National Bank of India, wants a custom interest rate calculator that uses the bank's proprietary formula instead of the default one provided by the auto-configuration.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.math.RoundingMode;

// Auto-configuration provides this default
// @ConditionalOnMissingBean(InterestCalculator.class)
public class DefaultInterestCalculator implements InterestCalculator {
    @Override
    public BigDecimal calculate(BigDecimal principal, BigDecimal rate, int months) {
        // Simple interest: P * R * T
        return principal.multiply(rate)
            .multiply(BigDecimal.valueOf(months))
            .divide(BigDecimal.valueOf(12), 2, RoundingMode.HALF_UP);
    }
}

// Rajesh defines his custom bean -- auto-config backs off
package com.bank.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BankConfig {

    @Bean
    public InterestCalculator interestCalculator() {
        return new CompoundInterestCalculator(); // Rajesh's custom implementation
    }
}
TEXT
Result: CompoundInterestCalculator is used, DefaultInterestCalculator is never created.
Rajesh's custom bean always wins over auto-configuration.

What is @SpringBootApplication?

@SpringBootApplication is a convenience annotation that combines three annotations into one. Without it, you would need to write all three separately on your main class.

| Component | Responsibility |
|---|---|
| @Configuration | Marks the class as a source of bean definitions |
| @EnableAutoConfiguration | Triggers classpath-based auto-configuration |
| @ComponentScan | Scans the package and sub-packages for annotated components |

JAVA
package com.bank;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// This single annotation is equivalent to:
// @Configuration
// @EnableAutoConfiguration
// @ComponentScan(basePackages = "com.bank")
@SpringBootApplication
public class NationalBankApplication {

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

Pankaj, the architect, placed the main class in the com.bank package. Because @ComponentScan scans the current package and all sub-packages by default, every component in com.bank.service, com.bank.repository, and com.bank.controller is discovered automatically.

DIAGRAM
com.bank/
├── NationalBankApplication.java    <-- @SpringBootApplication here
├── config/
│   └── BankConfig.java             <-- discovered by @ComponentScan
├── service/
│   └── AccountService.java         <-- discovered by @ComponentScan
├── repository/
│   └── AccountRepository.java      <-- discovered by @ComponentScan
└── controller/
    └── AccountController.java      <-- discovered by @ComponentScan

How is @SpringBootApplication different from @EnableAutoConfiguration?

@EnableAutoConfiguration is just one of the three parts inside @SpringBootApplication. Using @EnableAutoConfiguration alone triggers auto-configuration but does not scan for components or mark the class as a configuration source.

| Feature | @EnableAutoConfiguration | @SpringBootApplication |
|---|---|---|
| Triggers auto-configuration | Yes | Yes |
| Marks class as @Configuration | No | Yes |
| Scans for @Component classes | No | Yes |
| Typical usage | Rare, advanced scenarios | Standard main class |

JAVA
// WRONG: only auto-configuration, no component scanning
@EnableAutoConfiguration
public class BankApp {
    public static void main(String[] args) {
        SpringApplication.run(BankApp.class, args);
        // AccountService, AccountRepository etc. will NOT be found
    }
}

// CORRECT: auto-configuration + component scanning + configuration
@SpringBootApplication
public class BankApp {
    public static void main(String[] args) {
        SpringApplication.run(BankApp.class, args);
        // Everything in com.bank.* is discovered and configured
    }
}

What are the differences between Spring Framework, Spring MVC, and Spring Boot?

These three are often confused but they serve different purposes and sit at different layers.

| Aspect | Spring Framework | Spring MVC | Spring Boot |
|---|---|---|---|
| What it is | Core IoC and DI container | Web framework on Servlet API | Opinionated auto-config layer |
| Key feature | Bean management, AOP, transactions | @Controller, @RequestMapping, DispatcherServlet | Auto-configuration, embedded server, starters |
| Server | No server included | Requires external Tomcat/Jetty | Embedded Tomcat/Jetty/Undertow |
| Configuration | Manual XML or Java config | Manual web.xml and servlet config | Convention over configuration |
| Dependency management | Manual | Manual | Starter POMs with curated versions |
| Production features | None built-in | None built-in | Actuator, health checks, metrics |

Spring Framework is the foundation. It provides the IoC container, dependency injection, AOP, and transaction management. Spring MVC builds on top of it and adds the web layer with DispatcherServlet, controllers, and request mapping. Spring Boot builds on top of both and adds auto-configuration, an embedded server, starter dependencies, and production-ready features like Actuator.

JAVA
// Spring Framework (manual wiring)
package com.bank.service;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class AccountService {
    private final AccountRepository repository;

    // Manual constructor injection -- Spring Framework handles this
    public AccountService(AccountRepository repository) {
        this.repository = repository;
    }

    public BigDecimal getBalance(String accountId) {
        return repository.findById(accountId)
            .map(Account::getBalance)
            .orElseThrow(() -> new AccountNotFoundException(accountId));
    }
}

// Spring MVC (web layer)
package com.bank.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
public class AccountController {
    private final AccountService accountService;

    public AccountController(AccountService accountService) {
        this.accountService = accountService;
    }

    @GetMapping("/api/v1/accounts/{id}/balance")
    public BigDecimal getBalance(@PathVariable String id) {
        return accountService.getBalance(id);
    }
}

// Spring Boot (auto-configuration -- no manual DataSource, Tomcat, etc.)
// Just add spring-boot-starter-web and spring-boot-starter-data-jpa
// Spring Boot auto-configures DataSource, EntityManagerFactory, Tomcat

What are common mistakes with @Conditional?

The most common mistake is forgetting that @ConditionalOnMissingBean checks the bean type, not the bean name. If two beans of different types both satisfy the same interface, the conditional might not behave as expected.

JAVA
// WRONG: ConditionalOnProperty without havingValue defaults to checking
// if the property EXISTS, not if it is "true"
package com.bank.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class WrongConfig {

    @Bean
    @ConditionalOnProperty(name = "bank.fraud.enabled")
    public FraudCheckService fraudCheckService() {
        // This bean is created even if bank.fraud.enabled=false
        // because the property EXISTS (it is not missing)
        return new FraudCheckService();
    }
}

// CORRECT: Always specify havingValue to match the expected value
@Configuration
public class CorrectConfig {

    @Bean
    @ConditionalOnProperty(name = "bank.fraud.enabled", havingValue = "true")
    public FraudCheckService fraudCheckService() {
        // Created only when bank.fraud.enabled=true
        return new FraudCheckService();
    }
}

Another common mistake is placing @SpringBootApplication in a deep package. Since @ComponentScan scans the current package and sub-packages, a main class in com.bank.app will not discover beans in com.bank.service.

JAVA
// WRONG: main class in com.bank.app -- misses com.bank.service
package com.bank.app;

@SpringBootApplication
public class BankApp { }
// Only scans com.bank.app and its sub-packages

// CORRECT: main class in com.bank -- scans everything below
package com.bank;

@SpringBootApplication
public class BankApp { }
// Scans com.bank.service, com.bank.repository, com.bank.controller, etc.

How does auto-configuration work internally?

When @EnableAutoConfiguration is present, Spring Boot reads a file called META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (or spring.factories in older versions) from every JAR on the classpath. Each line in this file is a fully qualified class name of an auto-configuration class. Spring loads these classes and evaluates their conditional annotations to decide which beans to create.

Pankaj designed the interest rate module as a reusable starter. The starter JAR contains an auto-configuration class with @ConditionalOnClass and @ConditionalOnMissingBean. Any microservice that adds this starter to its dependencies gets a default InterestCalculator bean automatically, but can override it by defining its own.

JAVA
package com.bank.starter.interest;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
@ConditionalOnClass(InterestCalculator.class)
public class InterestAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public InterestCalculator interestCalculator() {
        return new SimpleInterestCalculator();
    }
}
TEXT
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.bank.starter.interest.InterestAutoConfiguration

What should a senior engineer know?

  • Auto-configuration ordering matters. Use @AutoConfigureBefore and @AutoConfigureAfter to control the order when one auto-configuration depends on another.
  • Use --debug flag or spring.boot.debug=true to see the auto-configuration report at startup. This report shows which auto-configurations were matched (positive matches) and which were skipped (negative matches), along with the exact condition that passed or failed.
  • Custom conditions can be created by implementing the Condition interface for complex business logic that built-in annotations cannot express.
  • In a microservices architecture at National Bank of India, Pankaj wraps shared configuration into custom starters with conditional annotations, so each service gets sensible defaults but can always override them.
BASH
# Run with debug to see auto-configuration report
java -jar national-bank-app.jar --debug
TEXT
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------
   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes (matched)

Negative matches:
-----------------
   RedisAutoConfiguration:
      - @ConditionalOnClass did not find required class (did not match)

What are common interview follow-up questions?

  • How does @ConditionalOnMissingBean know the order of bean registration? Auto-configuration classes are processed after user-defined @Configuration classes, so your beans are always registered first.
  • Can you combine multiple conditional annotations on one bean? Yes. All conditions must be true for the bean to be created (they are combined with AND logic).
  • How do you exclude a specific auto-configuration? Use @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) or set spring.autoconfigure.exclude in properties.
  • What is the difference between @ConditionalOnProperty(matchIfMissing = true) and matchIfMissing = false? With matchIfMissing = true, the bean is created even when the property is absent, making it opt-out. With false (default), the bean requires the property to exist.
JAVA
// Excluding a specific auto-configuration
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class NationalBankBatchApp {
    public static void main(String[] args) {
        SpringApplication.run(NationalBankBatchApp.class, args);
    }
}

Visual Overview

DIAGRAM
  ┌──────────────────────────────────────────────────┐
  │            @SpringBootApplication                 │
  │                                                   │
  │  ┌──────────────┐  ┌───────────────────────────┐ │
  │  │ @Configuration│  │ @EnableAutoConfiguration  │ │
  │  │               │  │                           │ │
  │  │ Marks class   │  │ Reads AutoConfiguration   │ │
  │  │ as bean source│  │ .imports from classpath    │ │
  │  └──────────────┘  └───────────────────────────┘ │
  │                                                   │
  │  ┌──────────────────────────────────────────────┐ │
  │  │            @ComponentScan                     │ │
  │  │  Discovers @Component, @Service, @Repository  │ │
  │  │  in current package and sub-packages          │ │
  │  └──────────────────────────────────────────────┘ │
  └──────────────────────────────────────────────────┘

  Auto-Configuration Decision Flow:

  ┌─────────────────────┐
  │  Read .imports file  │
  └────────┬────────────┘
           ▼
  ┌─────────────────────┐     No
  │ @ConditionalOnClass? ├──────────→ Skip bean
  └────────┬────────────┘
           │ Yes
           ▼
  ┌──────────────────────────┐     No
  │ @ConditionalOnMissingBean?├─────→ Skip bean (user defined one)
  └────────┬─────────────────┘
           │ Yes (no user bean)
           ▼
  ┌─────────────────────┐     No
  │ @ConditionalOnProperty? ├───────→ Skip bean
  └────────┬────────────┘
           │ Yes
           ▼
  ┌─────────────────────┐
  │  Register the bean   │
  └─────────────────────┘

Key Takeaway

The @Conditional annotation is the foundation of Spring Boot's auto-configuration. When Spring Boot starts, it reads auto-configuration classes from the classpath and evaluates their conditions. @ConditionalOnClass checks whether a library is available, which is how adding a starter dependency automatically triggers the matching configuration. @ConditionalOnMissingBean checks whether you have already defined a bean of the same type, and backs off if you have, so your custom configuration always wins. @ConditionalOnProperty checks a configuration value, enabling feature flags that control which beans are created without any if-else logic in code.

@SpringBootApplication is the single annotation that every Spring Boot main class carries. It combines @Configuration to mark the class as a bean source, @EnableAutoConfiguration to trigger classpath-based auto-configuration, and @ComponentScan to discover all annotated components in the package tree. Pankaj places the main class in the root package so every sub-package is scanned automatically.

Spring Framework provides the core IoC container with dependency injection and AOP. Spring MVC adds the web layer with DispatcherServlet and controllers on top of the Servlet API. Spring Boot adds opinionated auto-configuration, an embedded server, starter dependencies, and production-ready features like Actuator. In the National Bank of India's microservices, each service uses Spring Boot with conditional annotations to get sensible defaults from shared starters while retaining the ability to override any bean with a custom implementation.

Remember one sentence: @Conditional makes Spring Boot smart enough to configure itself based on what is on the classpath, what you have already defined, and what your properties say.

---

Share
Use ← → arrow keys to navigate