Skip to content

Auto-Configuration Interview Questions & Answers

16 questions Updated 2026-06-26 Share:

How Spring Boot auto-configuration works — @EnableAutoConfiguration, conditional beans, AutoConfiguration.imports, starters, and how to debug and override what gets configured.

Read the in-depth guideHow Spring Boot Auto-Configuration Actually Works(opens in new tab)
16 of 16

Auto-configuration is Spring Boot's mechanism for automatically configuring beans based on what is on the classpath, what beans already exist, and what properties are set. It is what lets a Spring Boot app run with almost no explicit @Bean definitions.

// Just add spring-boot-starter-web to the classpath and Spring Boot
// auto-configures: an embedded Tomcat, a DispatcherServlet, a Jackson
// ObjectMapper, an error page, and more — with zero @Bean methods.
@SpringBootApplication // ← bundles @EnableAutoConfiguration
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

Auto-configuration is opinionated but backs off — the moment you define your own bean of a given type, Spring Boot's default for that type steps aside. This "convention over configuration, but you can always override" model is the core of Spring Boot.

Rule of thumb: Auto-configuration = "sensible defaults wired from the classpath"; you only write configuration for the things you want to differ from those defaults.

@EnableAutoConfiguration imports AutoConfigurationImportSelector, which reads a list of candidate auto-configuration class names from a well-known file on the classpath and then filters them by their @Conditional annotations.

// Spring Boot 2.7+ reads this file from every jar on the classpath:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
//
// (Before 2.7 it read the "EnableAutoConfiguration" key in:
//  META-INF/spring.factories)
//
// Each line is a fully-qualified class name, e.g.:
// org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration

The selector loads all candidates, then applies each class's conditions (@ConditionalOnClass, @ConditionalOnMissingBean, etc.). Only the ones whose conditions pass actually contribute beans. This two-step "list, then filter" is why adding a starter jar is enough to switch features on.

Rule of thumb: @EnableAutoConfiguration doesn't scan your code — it reads AutoConfiguration.imports from library jars and lets @Conditional decide what applies.

@SpringBootApplication is a convenience meta-annotation that bundles the three annotations almost every app needs, so you don't repeat them.

// @SpringBootApplication is equivalent to:
@SpringBootConfiguration   // a specialization of @Configuration
@EnableAutoConfiguration   // turn on auto-configuration
@ComponentScan             // scan this package and sub-packages for @Component etc.
public class App { }

@SpringBootConfiguration marks the class as a source of bean definitions (and lets the test framework find it). @EnableAutoConfiguration enables the auto-config engine. @ComponentScan discovers your own @Component, @Service, @RestController, etc. Because component scanning starts in the annotated class's package, the main class should sit in a root package above all your code.

Rule of thumb: Put your @SpringBootApplication class in the top-level package — everything below it is component-scanned automatically.

Auto-configuration classes are gated by @Conditional annotations that decide, at startup, whether a configuration or bean should be created.

@AutoConfiguration
@ConditionalOnClass(DataSource.class)            // only if DataSource is on the classpath
public class MyDataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean                    // back off if the user defined one
    @ConditionalOnProperty(name = "app.db.enabled", havingValue = "true")
    DataSource dataSource() { /* ... */ }
}

The common ones: @ConditionalOnClass / @ConditionalOnMissingClass (classpath presence), @ConditionalOnBean / @ConditionalOnMissingBean (bean presence), @ConditionalOnProperty (config flag), @ConditionalOnWebApplication (servlet vs reactive vs none), and @ConditionalOnResource (a file exists). They all build on the lower-level @Conditional(Condition.class) SPI.

Rule of thumb: @ConditionalOnClass decides if a library is present; @ConditionalOnMissingBean decides whether you already overrode it — together they make auto-config both classpath-driven and user-overridable.

Most auto-configured beans are annotated @ConditionalOnMissingBean, meaning "only create this default if the application hasn't already provided one." Defining your own bean of the same type makes Spring Boot's default silently back off.

@Configuration
public class JacksonConfig {

    // Spring Boot auto-configures an ObjectMapper, but it's marked
    // @ConditionalOnMissingBean — so THIS one wins and the default backs off.
    @Bean
    ObjectMapper objectMapper() {
        return new ObjectMapper()
            .findAndRegisterModules()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
}

Ordering matters: user configuration is evaluated before auto-configuration (auto- config classes run last, via @AutoConfiguration ordering), so by the time the default bean's condition is checked, your bean already exists and the condition fails.

Rule of thumb: To customize an auto-configured component, just declare your own @Bean of that type — no need to disable anything; @ConditionalOnMissingBean does it.

A starter is a curated, empty "bag of dependencies" — a POM that pulls in a consistent, version-aligned set of libraries for a feature. Starters bring the jars; auto-configuration reacts to those jars being present.

<!-- This one line pulls in Spring MVC, embedded Tomcat, Jackson, validation, etc. -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- The starter itself has almost no code — its job is to declare dependencies.
     Because Tomcat + Spring MVC are now on the classpath, the matching
     @ConditionalOnClass auto-configurations activate automatically. -->

Starters follow the naming convention spring-boot-starter-*. The versions come from the Spring Boot BOM (bill of materials) inherited via the parent POM, so you rarely specify versions yourself.

Rule of thumb: Starters answer "what's on the classpath"; auto-configuration answers "what to do about it." Add a starter, get a working feature.

Spring Boot can print a condition evaluation report showing every auto-config class and why it matched ("Positive matches") or didn't ("Negative matches").

# application.properties — log the report at startup:
debug=true
// Or, at runtime, hit the Actuator endpoint (needs spring-boot-starter-actuator):
// GET /actuator/conditions
// Returns JSON of positiveMatches / negativeMatches / unconditionalClasses.
//
// You can also start with --debug on the command line:
// java -jar app.jar --debug

The report tells you exactly why something did or didn't configure — e.g. "DataSourceAutoConfiguration did not match: @ConditionalOnClass found required class 'javax.sql.DataSource' but ... no DataSource bean URL property." This is the first tool to reach for when "Spring Boot isn't configuring the thing I expect."

Rule of thumb: When auto-config does something surprising, set debug=true (or call /actuator/conditions) and read the positive/negative matches before guessing.

Sometimes an auto-configuration activates that you don't want (a classic case is DataSourceAutoConfiguration firing before you've configured a database). You can exclude it by class or by name.

// Option 1 — on the application class:
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class App { }
# Option 2 — in properties (use the fully-qualified name, by string):
spring.autoconfigure.exclude=\
  org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

Use exclude for classes on the classpath; use excludeName (or the property form) when the class might not be present at compile time. Excluding is a blunt instrument — prefer overriding a single bean with @ConditionalOnMissingBean semantics when you only need to change part of the behavior.

Rule of thumb: Exclude a whole auto-configuration only when you genuinely don't want the feature; to merely tweak it, override the specific bean instead.

@Configuration marks any class that defines @Bean methods. @AutoConfiguration is a specialized form (Spring Boot 2.7+) used only by library auto-configuration classes listed in AutoConfiguration.imports, and it adds ordering and proxy semantics suited to that role.

// Library code — a real auto-configuration:
@AutoConfiguration(after = DataSourceAutoConfiguration.class) // ordering hints
@ConditionalOnClass(JdbcTemplate.class)
public class JdbcTemplateAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    JdbcTemplate jdbcTemplate(DataSource ds) { return new JdbcTemplate(ds); }
}

// Your app code — ordinary configuration:
@Configuration
public class AppConfig { @Bean MyService myService() { return new MyService(); } }

@AutoConfiguration is meta-annotated with @Configuration(proxyBeanMethods = false) (lightweight, no CGLIB proxying) and supports before/after/beforeName/afterName to order auto-config classes relative to each other. You use @Configuration in apps; @AutoConfiguration is for the libraries you build.

Rule of thumb: Write @Configuration in your application; reserve @AutoConfiguration for reusable starter libraries that register themselves via AutoConfiguration.imports.

Order matters when one auto-config's beans depend on another's. Spring Boot provides ordering hints rather than a global sequence — you express "before" and "after" relationships.

@AutoConfiguration(
    after = DataSourceAutoConfiguration.class,   // run after the DataSource is configured
    before = TransactionAutoConfiguration.class) // but before transactions are set up
public class MyOrmAutoConfiguration { /* needs a DataSource to exist first */ }

// Use the *Name variants when the other class may be absent at compile time:
@AutoConfiguration(afterName =
    "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration")
class AnotherAutoConfiguration { }

There are also @AutoConfigureBefore, @AutoConfigureAfter, and @AutoConfigureOrder annotations (the older style). Note that auto-configurations as a group always run after your own user-defined @Configuration, which is what makes @ConditionalOnMissingBean back-off work.

Rule of thumb: Don't try to impose a total order — declare only the before/after edges your beans actually need, and let Spring Boot topologically sort the rest.

Conditions are evaluated during the bean definition registration phase of context refresh, before beans are instantiated. Class-presence conditions (@ConditionalOnClass) are checked using ASM bytecode inspection, not by loading the class — so a missing class doesn't throw NoClassDefFoundError.

// This is safe even if 'com.example.Optional' is NOT on the classpath:
@ConditionalOnClass(name = "com.example.OptionalLibrary")
@AutoConfiguration
public class OptionalAutoConfiguration {
    // The condition is read from annotation metadata via ASM, so the JVM
    // never tries to link OptionalLibrary unless the condition passes.
}

Because conditions run at definition time, @ConditionalOnBean is order-sensitive: the bean it looks for must already be defined when the condition is evaluated, which is why @ConditionalOnBean is mainly reliable inside auto-configuration (ordered after user config) rather than between two arbitrary user beans.

Rule of thumb: @ConditionalOnClass is safe for optional libraries (ASM-based, no linking); @ConditionalOnBean only sees beans defined before it — mind the ordering.

To make a reusable library that configures itself when added to any Spring Boot app, write an @AutoConfiguration class and register it in the imports file.

// 1. The auto-configuration class:
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean
    GreetingService greetingService(GreetingProperties props) {
        return new GreetingService(props.getPrefix());
    }
}

// 2. Register it (src/main/resources/META-INF/spring/
//    org.springframework.boot.autoconfigure.AutoConfiguration.imports):
// com.example.greeting.GreetingAutoConfiguration

Any app that adds your jar now gets a GreetingService automatically, overridable via @ConditionalOnMissingBean and configurable via GreetingProperties. This is exactly how official starters work. Package the auto-config separately from your "starter" POM by convention (acme-spring-boot-autoconfigure + acme-spring-boot-starter).

Rule of thumb: Custom auto-config = @AutoConfiguration class + a line in AutoConfiguration.imports + @ConditionalOnMissingBean so consumers can override.

Auto-configurations expose tunables through @ConfigurationProperties classes that bind external properties to typed Java objects, so users configure behavior without touching code.

@ConfigurationProperties(prefix = "app.greeting")
public class GreetingProperties {
    private String prefix = "Hello";   // default if app.greeting.prefix is unset
    private int repeat = 1;
    // getters/setters — binder uses them (or use a record + constructor binding)
}

// The auto-config enables and injects it:
@AutoConfiguration
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration { /* inject GreetingProperties into beans */ }
# User overrides in application.properties:
app.greeting.prefix=Hi
app.greeting.repeat=3

This is the standard pattern: the library ships defaults, the user overrides via properties/YAML/env vars, and relaxed binding maps APP_GREETING_PREFIX (env) to app.greeting.prefix automatically.

Rule of thumb: Expose every knob through @ConfigurationProperties, never hard-code — it's how auto-config stays "opinionated defaults you can always override."

A FailureAnalyzer turns an ugly startup stack trace into a readable diagnostic with a probable cause and an action. Spring Boot ships many; libraries can add their own. It's the reason a misconfigured port or missing datasource prints a clean explanation.

public class PortInUseFailureAnalyzer
        extends AbstractFailureAnalyzer<PortInUseException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, PortInUseException cause) {
        return new FailureAnalysis(
            "Web server failed to start. Port " + cause.getPort() + " was already in use.",
            "Identify and stop the process on port " + cause.getPort()
                + ", or configure a different port with 'server.port'.",
            cause);
    }
}
// Registered in META-INF/spring.factories under
// org.springframework.boot.diagnostics.FailureAnalyzer

When startup fails, Spring Boot runs each analyzer; the first that matches the exception prints the friendly APPLICATION FAILED TO START banner instead of a raw trace.

Rule of thumb: Those clear "Description / Action" startup error blocks are FailureAnalyzers — add one to your library to give users actionable config errors.

The embedded server is auto-configured by classpath presence, so you change it by changing dependencies — exclude Tomcat from the web starter and add the alternative.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion> <!-- drop the default Tomcat -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency> <!-- add Jetty; its @ConditionalOnClass auto-config now wins -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

No code changes are needed: ServletWebServerFactoryAutoConfiguration picks whichever server class is on the classpath. This is auto-configuration's whole philosophy — the classpath expresses intent, and the right beans follow.

Rule of thumb: To change an auto-configured infrastructure piece (server, JSON lib, connection pool), change the dependency on the classpath, not the Java code.

By default @Configuration classes are CGLIB-proxied so that calling one @Bean method from another returns the shared singleton. That proxying has a startup cost. Auto-config classes set proxyBeanMethods = false because they rarely call their own bean methods inter-dependently — they take dependencies as method parameters instead.

@Configuration(proxyBeanMethods = false) // no CGLIB subclass — faster startup
public class FastConfig {
    @Bean A a() { return new A(); }

    // Get 'A' via a parameter (the container injects the singleton),
    // NOT by calling a() directly — direct calls would create a new A
    // because there's no proxy to intercept them.
    @Bean B b(A a) { return new B(a); }
}

@AutoConfiguration already implies proxyBeanMethods = false. The trade-off: with proxying off you must never invoke another @Bean method directly (it would bypass the container and build a duplicate), so always inject collaborators as parameters.

Rule of thumb: proxyBeanMethods = false = faster, lighter config — fine as long as @Bean methods receive their dependencies as parameters rather than calling each other.

More ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel