Spring Boot Auto-Configuration — How It Works and How to Override It

by Eric Hanson, Backend Developer at Clean Systems Consulting

What auto-configuration actually does

Auto-configuration is the mechanism that makes spring-boot-starter-data-jpa wire up a DataSource, EntityManagerFactory, and transaction manager without any explicit bean definitions. Add the dependency, provide the connection properties, and the infrastructure appears.

The mechanism: Spring Boot reads a list of auto-configuration classes from every JAR on the classpath. Each class carries @Conditional annotations that determine whether it applies. The conditions check for the presence of classes, existing beans, property values, and resource files. Only conditions that pass cause the auto-configuration to apply.

The list of auto-configuration classes is in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (Spring Boot 2.7+, replacing spring.factories). Every Spring Boot starter ships this file — it's what makes starters self-configuring.

The @Conditional mechanism

Every auto-configuration class is annotated with one or more @Conditional annotations. The built-in conditions cover the common cases:

@ConditionalOnClass — applies only if a class is on the classpath:

@AutoConfiguration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
public class DataSourceAutoConfiguration {
    // Only applies if DataSource and EmbeddedDatabaseType are on the classpath
}

Adding spring-boot-starter-data-jpa to the classpath satisfies this condition. Without the starter, the condition fails and no database auto-configuration applies — cleanly.

@ConditionalOnMissingBean — applies only if a bean of the specified type doesn't already exist:

@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
    return properties.initializeDataSourceBuilder().build();
}

This is the override pattern: define your own DataSource bean and this auto-configured bean doesn't apply. The condition checks the bean type (DataSource), not the bean name — any DataSource bean, regardless of name, satisfies the condition.

@ConditionalOnProperty — applies only if a property has a specific value:

@ConditionalOnProperty(
    prefix = "spring.datasource",
    name = "url"
)
public DataSource dataSource(...) { ... }

@ConditionalOnWebApplication — applies only in a web application context.

@ConditionalOnMissingClass — the inverse of @ConditionalOnClass.

@ConditionalOnSingleCandidate — applies only if exactly one bean of the type exists (used for auto-configuration that wraps a uniquely-identified infrastructure bean).

Override pattern 1: define your own bean

The primary override mechanism is defining a @Bean of the same type. Auto-configuration checks for your bean first and backs off:

@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(System.getenv("DATABASE_URL"));
        config.setMaximumPoolSize(20);
        config.setConnectionTimeout(30_000);
        config.setLeakDetectionThreshold(60_000);
        return new HikariDataSource(config);
    }
}

Spring Boot's DataSourceAutoConfiguration sees your DataSource bean, the @ConditionalOnMissingBean(DataSource.class) condition fails, and the auto-configured bean is not created. Your configuration applies.

The subtlety: @ConditionalOnMissingBean checks the application context for the bean type at the point the condition is evaluated. User-defined beans are processed before auto-configuration classes — this is why defining your own bean reliably suppresses the auto-configured one.

Override pattern 2: properties

Most auto-configuration behavior is controlled by @ConfigurationProperties classes. Overriding a property changes the behavior without defining a new bean:

# Override auto-configured connection pool settings
spring:
  datasource:
    hikari:
      maximum-pool-size: 20
      connection-timeout: 30000
      leak-detection-threshold: 60000
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
        jdbc:
          batch_size: 50

The auto-configuration still applies — but its behavior is shaped by the properties rather than its defaults. This is the right choice when the auto-configured infrastructure is correct but needs tuning.

Override pattern 3: exclude specific auto-configuration

When you want to prevent auto-configuration from applying entirely — not override a specific bean, but disable the entire auto-configuration class:

@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Or via properties (useful when the auto-configuration class is in a dependency you don't control):

spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
      - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

Exclusion is appropriate when the auto-configuration conflicts with a manual setup and @ConditionalOnMissingBean overrides are insufficient. A common case: an application that uses multiple DataSource beans for different databases — Spring Boot's single DataSource auto-configuration doesn't fit, so exclude it and wire the data sources manually.

Auto-configuration ordering

Auto-configuration classes are not applied in arbitrary order. The @AutoConfiguration annotation (and @AutoConfigureBefore, @AutoConfigureAfter) controls ordering between auto-configuration classes:

@AutoConfiguration(after = DataSourceAutoConfiguration.class)
public class HibernateJpaAutoConfiguration {
    // Applies after DataSource is available — can safely reference DataSource
}

User-defined beans and @Configuration classes are processed before auto-configuration classes. This is the ordering guarantee that makes @ConditionalOnMissingBean reliable — user beans exist when auto-configuration conditions are evaluated.

The ordering within user-defined @Configuration classes: Spring processes them in the order they're registered, which is influenced by component scanning order (not alphabetical, not predictable). If two @Configuration classes define beans where order matters, use @DependsOn or restructure to eliminate the order dependency.

Writing your own auto-configuration

Auto-configuration isn't just for framework code — it's the right mechanism for wiring shared infrastructure in libraries used across multiple Spring Boot applications:

// In your shared library
@AutoConfiguration
@ConditionalOnClass(AuditService.class)
@ConditionalOnMissingBean(AuditService.class)
@EnableConfigurationProperties(AuditProperties.class)
public class AuditAutoConfiguration {

    @Bean
    public AuditService auditService(AuditProperties properties) {
        return new DefaultAuditService(properties.getStorageBackend());
    }
}

Register it in your library's META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:

com.example.audit.AuditAutoConfiguration

Applications that include your library on the classpath get AuditService automatically. Applications that define their own AuditService bean get their version instead. The library's behavior is replaceable without modification.

The conditions evaluation report — the diagnostic tool

When a bean isn't being created and you don't know why, the conditions evaluation report shows exactly which conditions passed and which failed:

# application.properties
debug=true
# or
logging.level.org.springframework.boot.autoconfigure=DEBUG

This produces output like:

CONDITIONS EVALUATION REPORT

Positive matches:
-----------------
DataSourceAutoConfiguration matched:
   - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)

Negative matches:
-----------------
MongoAutoConfiguration:
   Did not match:
      - @ConditionalOnClass did not find required class 'com.mongodb.MongoClient' (OnClassCondition)

Exclusions:
-----------
None

Unconditional classes:
----------------------
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

"Positive matches" shows what applied. "Negative matches" shows what didn't and why. "Exclusions" shows what was explicitly excluded.

The three diagnostic questions the report answers:

  1. Why isn't my bean being created? Look in negative matches — find your auto-configuration class and read the failed condition.
  2. Why are there two beans of the same type? Look in positive matches — both your bean definition and the auto-configured bean may be applying.
  3. What auto-configuration is the classpath triggering? Scan positive matches for unexpected auto-configuration.

The override decision

The three patterns serve different needs:

Define your own bean (@ConditionalOnMissingBean) when you need different behavior or a different implementation — a custom DataSource with specific pool settings, a custom ObjectMapper with different serialization configuration.

Override properties when the auto-configured implementation is correct but needs tuning — Hikari pool size, JPA batch size, cache TTLs.

Exclude the auto-configuration when it conflicts with a manual setup or when its presence would cause errors — multiple data sources, custom transaction management, third-party infrastructure that conflicts with Spring Boot's defaults.

The conditions evaluation report is the diagnostic tool for all three — it shows what applied, what didn't, and why. With it, auto-configuration is a transparent system with inspectable behavior, not a black box.

Scale Your Backend - Need an Experienced Backend Developer?

We provide backend engineers who join your team as contractors to help build, improve, and scale your backend systems.

We focus on clean backend design, clear documentation, and systems that remain reliable as products grow. Our goal is to strengthen your team and deliver backend systems that are easy to operate and maintain.

We work from our own development environments and support teams across US, EU, and APAC timezones. Our workflow emphasizes documentation and asynchronous collaboration to keep development efficient and focused.

  • Production Backend Experience. Experience building and maintaining backend systems, APIs, and databases used in production.
  • Scalable Architecture. Design backend systems that stay reliable as your product and traffic grow.
  • Contractor Friendly. Flexible engagement for short projects, long-term support, or extra help during releases.
  • Focus on Backend Reliability. Improve API performance, database stability, and overall backend reliability.
  • Documentation-Driven Development. Development guided by clear documentation so teams stay aligned and work efficiently.
  • Domain-Driven Design. Design backend systems around real business processes and product needs.

Tell us about your project

Our offices

  • Copenhagen
    1 Carlsberg Gate
    1260, København, Denmark
  • Magelang
    12 Jalan Bligo
    56485, Magelang, Indonesia

More articles

How Domain-Driven Architecture Helps Manage Complex APIs

Complex APIs aren’t just about endpoints—they’re about rules, workflows, and integrations. Domain-driven architecture helps keep that complexity under control.

Read more

Auckland Keeps Losing Its Best Backend Developers to Sydney and London — Here Is How Startups Adapt

Your best backend engineer just told you she's moving to Melbourne. The one before her went to London. You're running out of people to lose.

Read more

Feeling Underqualified? How to Fake Confidence (Safely)

Everyone feels underqualified sometimes, especially early in their career. Here’s how to appear confident without pretending to be an expert you’re not.

Read more

How Professional Remote Teams Respect Each Other’s Time

Time is the most valuable resource for remote teams. Professional teams know that respecting it leads to higher productivity and less stress.

Read more