Mid-level 3 min · March 09, 2026

Spring Boot Auto-Configuration: Missing HikariCP, No Error

A transitive dependency removed HikariCP; auto-config silently skipped DataSource, causing NPE.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Auto-Configuration is conditional bean wiring — Spring Boot loads config classes from JAR dependencies but only executes them if classpath and property conditions are met
  • @EnableAutoConfiguration triggers a scan of META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports (or spring.factories in older versions) across all JARs
  • @ConditionalOnClass activates config only when a specific class exists on the classpath — this is how starters adapt to your dependencies
  • @ConditionalOnMissingBean provides a default 'opinion' but steps aside when you define your own bean — this is the override mechanism
  • The Conditions Evaluation Report (debug=true) shows every positive and negative match with the exact reason each config was accepted or rejected
  • The biggest mistake: defining a bean that conflicts with auto-config without understanding @ConditionalOnMissingBean should have skipped the default
Plain-English First

Imagine you buy a high-end smart home kit. In the old days — Standard Spring — you had to manually wire the doorbell to the speaker, the lights to the switch, and the thermostat to the heater using a thick manual nobody wanted to read. With Spring Boot Auto-Configuration, the system is smart in the way a good contractor is smart: it walks into the room, sees what is already there, and makes sensible connections without asking you to approve each wire.

It looks around and says: 'I see a lightbulb and a switch — I will connect them.' If you have already installed your own professional dimmer switch — a custom bean — the system notices and steps back: 'You have this covered, I will leave yours in place.' That is not magic. That is conditional logic that respects your decisions. Understanding this distinction is what separates engineers who fight Spring Boot from engineers who ship with it.

Auto-Configuration is the mechanism that allows Spring Boot to achieve its 'just run it' experience. While critics call it magic, it is a predictable sequence of conditional logic gates that evaluate your classpath, your existing beans, and your application properties at startup — in that order.

Misunderstanding auto-configuration causes real production failures. A missing classpath dependency silently skips a critical config class and you find out at 2 AM when every database call is throwing NullPointerException. A conflicting user-defined bean triggers NoUniqueBeanDefinitionException in an environment you cannot reproduce locally. A slow startup caused by evaluating hundreds of unnecessary conditions costs 30 seconds per deployment multiplied across 50 Kubernetes replicas — time nobody budgeted for.

I have debugged all three of these. What they share is that the information was available the whole time in the Conditions Evaluation Report. The engineers involved just did not know to look there.

This guide deconstructs the spring-boot-autoconfigure module to show exactly how the framework manages bean creation using the @Conditional ecosystem. By the end, you will understand how to write your own auto-configurations, read the Conditions Report without panic, and debug missing bean issues without guessing.

The Mechanics of Auto-Configuration: Classpath Discovery

When you annotate your main class with @SpringBootApplication, you are implicitly enabling @EnableAutoConfiguration. This triggers a search for a file named META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports in Spring Boot 3.x, or META-INF/spring.factories in Spring Boot 2.x, across every JAR on your classpath.

Inside these files is a flat list of fully qualified configuration class names. Spring Boot attempts to load all of them. Here is where the conditional logic takes over: every class in that list is annotated with one or more @Conditional annotations that are evaluated before any @Bean method is executed. A configuration class only runs its @Bean methods if every condition on the class passes. If a single condition fails — classpath missing a class, property not set, required bean absent — the entire configuration class is skipped. Silently.

This is the mechanism that makes a single spring-boot-starter-data-jpa dependency configure DataSource, EntityManagerFactory, TransactionManager, and Hibernate dialect without you writing a line of configuration XML. It is also the mechanism that silently does nothing when HikariCP disappears from your resolved classpath due to a dependency conflict.

io/thecodeforge/autoconfig/ForgeDbAutoConfiguration.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package io.thecodeforge.autoconfig;

import org.springframework.boot.autoconfigure.AutoConfiguration;
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 javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariConfig;

/**
 * io.thecodeforge: Production-grade conditional configuration.
 *
 * Three conditions must ALL pass for this class to execute:
 *   1. HikariDataSource must be on the classpath
 *   2. The property forge.database.enabled must be true (or absent — matchIfMissing)
 *   3. No DataSource bean may already exist in the context
 *
 * If any condition fails, this entire class is silently skipped.
 * No error. No warning. No log line at INFO or WARN level.
 * The Conditions Evaluation Report at debug=true is the only place this skip is recorded.
 */
@AutoConfiguration  // Preferred over @Configuration for auto-config classes in Spring Boot 3.x
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnProperty(
    name = "forge.database.enabled",
    havingValue = "true",
    matchIfMissing = true  // Activates even when property is absent — opt-out model
)
public class ForgeDbAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(DataSource.class)  // Class-level check — if ANY DataSource bean exists, skip this
    public DataSource dataSource(ForgeDataSourceProperties props) {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(props.getUrl());
        config.setUsername(props.getUsername());
        config.setPassword(props.getPassword());
        config.setMaximumPoolSize(props.getMaxPoolSize());
        config.setConnectionTimeout(props.getConnectionTimeoutMs());
        return new HikariDataSource(config);
    }
}
Output
// Startup log output with debug=true — Positive Match case:
//
// ForgeDbAutoConfiguration matched:
// - @ConditionalOnClass found required class 'com.zaxxer.hikari.HikariDataSource'
// - @ConditionalOnProperty (forge.database.enabled=true) matched
// - @ConditionalOnMissingBean (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans
//
// Negative Match case — when HikariCP is missing from classpath:
//
// ForgeDbAutoConfiguration:
// Did not match:
// - @ConditionalOnClass did not find required class 'com.zaxxer.hikari.HikariDataSource'
// Not evaluated:
// - @ConditionalOnProperty not checked (class condition failed first)
// - @ConditionalOnMissingBean not checked (class condition failed first)
Auto-Configuration is a Bouncer, Not a Dictator
  • @ConditionalOnClass checks if a specific class exists on the classpath — evaluated before the Spring context is fully initialized, making it the cheapest condition to evaluate
  • @ConditionalOnMissingBean checks if you already defined a bean of that type — your bean wins unconditionally, the auto-configured default steps aside without complaint
  • @ConditionalOnProperty checks if a property is set to a specific value — enables runtime toggling between implementations without code changes
  • @ConditionalOnBean is the inverse of @ConditionalOnMissingBean — config only activates if a specific prerequisite bean already exists in the context
  • @ConditionalOnWebApplication skips configuration entirely for non-web contexts like batch jobs or CLI runners — prevents web-only beans from polluting a non-web context
  • Multiple @Conditional annotations on the same class compose with AND logic — every condition must pass, and evaluation stops at the first failure
Production Insight
A transitive dependency conflict removed HikariCP from the classpath in a CI environment but not locally, because developers had a cached Gradle dependency that was not present in the clean CI build. @ConditionalOnClass(HikariDataSource.class) evaluated to false in CI and silently skipped the DataSource config. The application started, passed health checks that did not verify the DataSource bean, and made it to production. Every database call threw NullPointerException. The fix took 10 minutes once the Conditions Report was checked. The investigation took 4 hours because nobody knew to look there.
Key Takeaway
Auto-Configuration is not magic — it is conditional bean wiring driven by classpath inspection, property resolution, and existing bean detection.
@ConditionalOnClass is the primary gatekeeper and the most common source of silent configuration skips — if the library class is absent from the classpath, the entire config class is skipped with no log output at default log levels.
@ConditionalOnMissingBean is the override mechanism — your beans always take priority over Spring Boot's opinions, which is the design intent: Boot provides defaults, you provide overrides.
Choosing the Right @Conditional Annotation
IfConfig depends on a library being present on the classpath — e.g., Redis client, HikariCP, Kafka
UseUse @ConditionalOnClass — evaluated against the classpath before the context initializes, cheapest evaluation, silently skips if the class is absent
IfConfig should provide a sensible default but yield completely to a user-defined bean of the same type
UseUse @ConditionalOnMissingBean on the @Bean method — user-defined beans always win, the framework default is skipped without conflict
IfConfig should be toggleable via application.properties or environment variables — feature flag pattern
UseUse @ConditionalOnProperty with havingValue and consider matchIfMissing=true for opt-out or matchIfMissing=false for opt-in behavior
IfConfig depends on another Spring-managed bean already existing in the context — dependent configuration
UseUse @ConditionalOnBean — evaluated against the live ApplicationContext, ensures the prerequisite bean was created before this config runs
IfConfig should only run in a Servlet or Reactive web application context
UseUse @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) — prevents web beans from appearing in batch jobs or CLI tools
IfConfig should only run when a specific expression evaluates to true — complex conditional logic
UseUse @ConditionalOnExpression with a SpEL expression — use sparingly, it is harder to test and debug than the typed conditionals above

Debugging the Magic: The Conditions Evaluation Report

The biggest source of frustration with auto-configuration is not that it fails — it is that it fails silently. A condition evaluates to false, the config class is skipped, and Spring Boot moves on without logging anything at INFO level. You are left with a missing bean and no obvious explanation.

The Conditions Evaluation Report fixes this. Add debug=true to application.properties and restart. Spring Boot will print every auto-configuration class it evaluated, categorized into Positive Matches (ran and created beans), Negative Matches (skipped and why), and Unconditional Classes (always run regardless of conditions). Each entry in Negative Matches shows the exact @Conditional annotation that failed and the value it tested against.

This report is also available at runtime without a restart via the Actuator /actuator/conditions endpoint. The JSON format is parseable, diffable between deployments, and can be integrated into your CI pipeline to detect configuration drift.

I treat the Conditions Report as the first tool, not the last resort. When a bean is missing, I check the report before I check the code.

io/thecodeforge/debug/ForgeStartupValidator.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package io.thecodeforge.debug;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;

/**
 * io.thecodeforge: Startup validation that fails fast if critical beans are absent.
 *
 * This pattern catches silent auto-configuration skips at boot time
 * instead of at runtime under production traffic.
 *
 * Place in the root application package so @ComponentScan picks it up.
 * Remove from test contexts with @Profile("!test") if needed.
 */
@Component
public class ForgeStartupValidator implements ApplicationRunner {

    private final ApplicationContext context;

    public ForgeStartupValidator(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void run(ApplicationArguments args) {
        validateCriticalBean(DataSource.class,
            "DataSource bean is absent. Check the Conditions Evaluation Report: " +
            "run with debug=true or query /actuator/conditions. " +
            "Likely cause: HikariCP missing from resolved classpath due to dependency conflict.");
    }

    private void validateCriticalBean(Class<?> type, String failureMessage) {
        if (context.getBeanNamesForType(type).length == 0) {
            throw new IllegalStateException(failureMessage);
        }
    }
}
Output
// When DataSource bean is absent — application fails at boot with actionable message:
//
// APPLICATION FAILED TO START
//
// Description:
// ForgeStartupValidator detected a missing critical bean.
//
// IllegalStateException: DataSource bean is absent. Check the Conditions Evaluation Report:
// run with debug=true or query /actuator/conditions.
// Likely cause: HikariCP missing from resolved classpath due to dependency conflict.
//
// This failure at boot prevents the silent NullPointerException under production load.
//
// Conditions Evaluation Report excerpt (with debug=true):
//
// Negative matches:
// DataSourceAutoConfiguration:
// Did not match:
// - @ConditionalOnClass did not find required class 'com.zaxxer.hikari.HikariDataSource'
//
// Action: Add explicit HikariCP dependency to build.gradle:
// implementation 'com.zaxxer:HikariCP:5.1.0'
Do Not Confuse Auto-Configuration with Component Scanning
Component Scanning finds classes in your own package tree — it looks for @Component, @Service, @Repository, @Controller and registers them as beans. It runs during ApplicationContext initialization and is scoped to packages you control. Auto-Configuration finds configuration instructions provided by JARs on your classpath — it reads import files and evaluates @Conditional annotations against the classpath, properties, and partial context state. It is driven by the framework, not by your package structure. They are separate mechanisms that run at different phases of startup. A common mistake is annotating a custom auto-configuration class with @Component instead of registering it in the imports file. Component scanning will not find it in a library JAR. The imports file is the only discovery mechanism for auto-configuration.
Production Insight
A platform team maintained a shared library that provided a pre-configured tracing client as an auto-configuration. The library was on the classpath but the tracing client was never initialized. The auto-configuration class existed, the @Conditional annotations were correct, but the class was registered in spring.factories under the wrong key — using org.springframework.boot.autoconfigure.EnableAutoConfiguration spelled incorrectly by one character. Spring Boot could not match the key during discovery, silently skipped the file, and the configuration was never loaded. Four hours of investigation. The fix was correcting a typo in one properties file.
Key Takeaway
The Conditions Evaluation Report with debug=true is the single most valuable debugging tool in the Spring Boot toolkit. Check it first, not last.
Positive Matches confirm what was configured and which conditions passed. Negative Matches show what was skipped and the exact condition that rejected the class. Unconditional Classes show configurations that always run regardless of your setup.
If a bean you expect is missing and you have not read the Conditions Report, you are guessing. Stop guessing.
● Production incidentPOST-MORTEMseverity: high

The Missing DataSource — Silent Auto-Configuration Skip

Symptom
Application started cleanly — no exceptions, no warnings, green health check on the deployment pipeline. Every repository call then threw NullPointerException on EntityManager injection. The database component was absent from the health endpoint response entirely — not DOWN, not UNKNOWN, just not there. The first sign anything was wrong was a Sentry alert from production traffic, not from the deployment itself.
Assumption
The team had spring-boot-starter-data-jpa in build.gradle and the datasource URL correctly set in application.properties. Both boxes were checked. The initial hypothesis was a Spring Boot version mismatch introduced during a library upgrade the week before. Nobody looked at the dependency tree because the application had compiled and started without complaint.
Root cause
A transitive dependency conflict had quietly removed HikariCP from the effective resolved classpath. A third-party analytics library pinned an older HikariCP version that was then excluded by a blanket resolution strategy in build.gradle. DataSourceAutoConfiguration carries @ConditionalOnClass(HikariDataSource.class). When HikariDataSource was absent, the entire configuration class was skipped — no error logged, no warning emitted, no startup failure. The condition evaluated to false and Spring Boot moved on. The DataSource bean simply did not exist, and nothing complained until actual traffic arrived.
Fix
HikariCP was declared as an explicit dependency with a pinned version in build.gradle instead of relying on transitive resolution through the starter. The Conditions Evaluation Report with debug=true confirmed DataSourceAutoConfiguration had moved to Positive Matches after the fix. A startup validation bean was added that injects DataSource and fails fast with a clear error message if the bean is absent — a pattern borrowed from Spring's own HealthIndicator design. The deployment pipeline now runs ./gradlew dependencies | grep hikari as a sanity step before building the Docker image.
Key lesson
  • Auto-Configuration silently skips classes when @ConditionalOnClass fails — no error, no warning, no log line at any level. The absence is the only signal.
  • Always check the Conditions Evaluation Report with debug=true when a bean you expect is not present. The negative matches section shows exactly which condition failed and what value was evaluated.
  • Never rely on transitive dependency resolution for runtime-critical libraries like HikariCP, Jackson, or Hibernate. Declare them explicitly with a pinned version in your build file.
  • Add startup assertions for critical infrastructure beans. ApplicationContext.getBean() inside an ApplicationRunner throws a meaningful error at boot time instead of a cryptic NullPointerException under production load.
  • Make the Conditions Evaluation Report part of your incident runbook. A five-second grep against startup logs resolves in minutes what otherwise takes hours.
Production debug guideWhen auto-configuration behaves unexpectedly, here is how to go from observable symptom to a verified resolution without guessing.6 entries
Symptom · 01
Bean you expect is not created — no error at startup but injection fails at runtime
Fix
Enable debug mode: add debug=true to application.properties. Restart and check the Conditions Evaluation Report for your config class under Negative Matches. The report prints the exact @Conditional annotation that evaluated to false and the value it compared against. Fix the condition — add the missing dependency, correct the property value, or remove the conflicting bean — and confirm the class moves to Positive Matches on the next startup.
Symptom · 02
NoUniqueBeanDefinitionException — two beans of the same type exist at startup
Fix
Determine whether you defined a bean that overlaps with an auto-configured one. If both are legitimate, add @Primary to the one that should win resolution by default, or use @Qualifier at every injection point that needs the non-primary bean. If the auto-configured bean is redundant, exclude it cleanly: @SpringBootApplication(exclude = {ConflictingAutoConfig.class}). Do not suppress the exception with @SuppressWarnings — it means two beans of the same type exist and something will get the wrong one eventually.
Symptom · 03
Application starts but auto-configured feature does not work — for example, Jackson serialization ignores your expected format
Fix
Check whether your custom ObjectMapper bean caused JacksonAutoConfiguration to skip its configuration via @ConditionalOnMissingBean. If your ObjectMapper does not configure the same settings as the auto-configured one, the difference will be silent and environment-specific. Verify which bean is active: curl localhost:8080/actuator/beans | jq '.contexts.*.beans | to_entries[] | select(.key | contains("jackson"))'. Then verify the bean's actual configuration by printing its registered modules at startup.
Symptom · 04
Slow startup — application takes 15 or more seconds to boot in an environment where 3 seconds is expected
Fix
Count the negative matches in the Conditions Evaluation Report. Each rejected auto-configuration still costs evaluation time — classpath checks, property lookups, bean context queries. If you are evaluating RabbitAutoConfiguration, MongoAutoConfiguration, and KafkaAutoConfiguration on a service that uses none of them, that is wasted time per replica per deployment. Exclude them explicitly: @SpringBootApplication(exclude = {RabbitAutoConfiguration.class, MongoAutoConfiguration.class}). Validate the impact with a before-and-after startup time comparison using spring.jmx.enabled=false and lazy initialization where appropriate.
Symptom · 05
Custom auto-configuration in a library JAR is never loaded even though the JAR is on the classpath
Fix
Verify the configuration class is registered in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports for Spring Boot 3.x, or META-INF/spring.factories under the key org.springframework.boot.autoconfigure.EnableAutoConfiguration for Spring Boot 2.x. Without registration, Spring Boot never reads the class regardless of its annotations. This file must be on the classpath root of the JAR — check the built artifact with jar tf your-library.jar | grep imports to confirm it is packaged correctly.
Symptom · 06
Property-based condition not matching despite the property being set in application.properties
Fix
Three things to verify in order. First, check the property name character-for-character including hyphens versus underscores — Spring Boot's relaxed binding does not apply to @ConditionalOnProperty name matching in all cases. Second, check the havingValue — if havingValue is 'true' and your property is set to 'enabled', the condition fails silently. Third, run curl localhost:8080/actuator/env/your.property.name to see the resolved value and which property source wins. Environment variables follow a different naming convention and frequently override application.properties in containerized environments without the team realizing it.
★ Auto-Configuration Debug Cheat Sheet — Commands That Save HoursReal commands for debugging Spring Boot auto-configuration issues. These are the commands I reach for first when a bean is missing or a feature is silently absent. Copy them into your team's runbook.
Need to see which auto-configurations matched and which were rejected
Immediate action
Enable debug mode in application.properties and check the Conditions Evaluation Report in startup logs
Commands
grep 'debug=true' src/main/resources/application.properties || echo 'debug=true' >> src/main/resources/application.properties
java -jar app.jar 2>&1 | grep -A2 'Negative matches' | head -50
Fix now
Find your expected config class name under Negative Matches. The report prints the exact @Conditional annotation that failed, the class or property it evaluated, and the result. That is your fix target — not the bean definition, the condition.
Need to verify if a specific auto-configured bean exists in the running application without restarting+
Immediate action
Query the Actuator beans endpoint and filter by type — no restart required
Commands
curl -s http://localhost:8080/actuator/beans | jq '.contexts.*.beans | to_entries[] | select(.value.type | contains("DataSource"))'
curl -s http://localhost:8080/actuator/conditions | jq '.positiveMatches | keys'
Fix now
If the bean type is absent from the beans output and the config class is absent from positiveMatches, check negativeMatches for the rejection reason. The conditions endpoint returns the same data as the startup Conditions Report but as structured JSON you can parse and alert on.
Need to quantify how many auto-configurations are being evaluated to diagnose startup performance+
Immediate action
Use the Actuator conditions endpoint to count positive and negative matches without reading walls of log output
Commands
curl -s http://localhost:8080/actuator/conditions | jq '.positiveMatches | length'
curl -s http://localhost:8080/actuator/conditions | jq '.negativeMatches | length'
Fix now
If negativeMatches is above 400 or 500 on a focused microservice, you are evaluating configurations for technologies you do not use. Exclude them with @SpringBootApplication(exclude={...}) or spring.autoconfigure.exclude in application.properties. Re-measure startup time after each exclusion batch — the gains compound.
Need to verify which property source is winning for a property that controls an auto-configuration condition+
Immediate action
Inspect the resolved property value and its source via Actuator — environment variables in containers frequently shadow application.properties silently
Commands
curl -s http://localhost:8080/actuator/env/spring.datasource.url | jq '.property'
curl -s http://localhost:8080/actuator/env | jq '.propertySources[] | select(.name | contains("applicationConfig"))'
Fix now
If the property source shown is systemEnvironment or kubernetes instead of applicationConfig, an environment variable or a Kubernetes ConfigMap is overriding your file-based value. The env endpoint shows the full resolution chain — check every source above applicationConfig in the list, because Spring Boot evaluates them in priority order and stops at the first match.
Standard Spring vs. Spring Boot Auto-Configuration
FeatureStandard Spring (Manual)Spring Boot (Auto-Config)
Setup EffortHigh: Every bean must be declared explicitly in XML or @Configuration classes. Nothing is assumed about your intent.Minimal: Starter dependencies include auto-configuration logic that activates based on what is present on the classpath.
Classpath AwarenessNone: You must manually ensure @Bean definitions match the JARs you have added. Mismatches cause NoClassDefFoundError at runtime.High: @ConditionalOnClass evaluates the classpath at startup and adapts configuration to match exactly what dependencies are present.
OverrideabilityExplicit: You change the source XML or @Configuration class directly. Changes are visible in version control. No hidden behavior.Seamless: @ConditionalOnMissingBean detects your bean at startup and steps aside. The override is invisible unless you check the Conditions Report.
VisibilityExplicit: All beans are declared in files you own. Reading the config tells you exactly what is in the context.Implicit: Beans are created by framework code you did not write. Requires Actuator /actuator/beans and debug=true to see the full picture.
Startup PerformancePredictable: Only the beans you declared are created. No conditional evaluation overhead beyond what you wrote.Variable: Hundreds of auto-configuration classes are evaluated at startup. Unused evaluations add overhead. Exclusions reduce this cost measurably.
Debugging ComplexityLow: Missing bean means you forgot to declare it. The fix is obvious — add the @Bean definition.Higher: Missing bean could mean a failed condition, a wrong property value, a classpath conflict, or a missing registration file. Requires systematic investigation via the Conditions Report.

Key takeaways

1
Auto-Configuration is not magic
it is a collection of @Configuration classes loaded from JAR metadata files and executed only when every @Conditional annotation on the class evaluates to true against the current classpath, properties, and context state.
2
The classpath is the primary driver of behavior. If a library is detected via @ConditionalOnClass, Spring Boot configures it. If the library class is absent
including due to a transitive dependency conflict — the configuration is silently skipped with no error at any log level.
3
User-defined beans always take priority over auto-configured defaults. @ConditionalOnMissingBean detects your bean at startup and skips the framework default. This is the intended override mechanism
understand it rather than fight it.
4
The Conditions Evaluation Report via debug=true is the most important diagnostic tool in the Spring Boot toolkit. It shows every positive match, every negative match with the exact failed condition, and every unconditional class. Check it first when a bean is missing, not after exhausting every other theory.
5
@ConditionalOnClass failing silently is the most common cause of missing infrastructure beans in production. Always declare critical runtime dependencies explicitly in your build file. Never rely on transitive dependency resolution for HikariCP, Hibernate, Jackson, or similar libraries.
6
Exclude irrelevant auto-configurations with @SpringBootApplication(exclude={...}) or spring.autoconfigure.exclude to reduce startup time. Each evaluated-but-rejected configuration costs real clock time multiplied across every replica and every deployment.

Common mistakes to avoid

5 patterns
×

Defining a bean that conflicts with auto-config without understanding @ConditionalOnMissingBean

Symptom
NoUniqueBeanDefinitionException at startup — two beans of the same type exist and Spring cannot decide which to inject. Or the auto-configured feature silently stops working because your bean replaced the default but does not replicate all of its configuration, such as registered Jackson modules or Hikari pool sizing.
Fix
Understand that @ConditionalOnMissingBean is the framework's way of yielding to you — but only if you define your bean before the condition is evaluated. If both beans still exist, add @Primary to yours so Spring has a clear winner for unqualified injection points, or use @Qualifier at every injection point that needs specificity. For a permanent resolution, exclude the conflicting auto-config: @SpringBootApplication(exclude = {ConflictingAutoConfig.class}). This is cleaner than @Primary because it removes the ambiguity entirely rather than resolving it at the injection point.
×

Forgetting to register custom auto-configuration in the imports file

Symptom
Custom auto-configuration class in a library JAR is never loaded. No error, no warning, no negative match in the Conditions Report — Spring Boot simply never reads the class. Beans that should be auto-configured are missing at runtime and the Conditions Report gives no information because the class was never evaluated.
Fix
Register the fully qualified class name in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports for Spring Boot 3.x — one fully qualified class name per line, no key-value syntax. For Spring Boot 2.x, use META-INF/spring.factories with the key org.springframework.boot.autoconfigure.EnableAutoConfiguration. Verify the file is packaged correctly by running jar tf your-library.jar | grep -E '(imports|factories)' against the built artifact. A file that exists in src/main/resources but is excluded by a build configuration rule will not appear in the JAR.
×

Over-reliance on auto-configuration without auditing what is being evaluated at startup

Symptom
Application takes 15 or more seconds to start in environments where 3 seconds is expected and budgeted. Kubernetes liveness probes time out during slow deployments. Cold start latency is unacceptable for serverless or Lambda deployments. Nobody knows which auto-configurations are causing the overhead because nobody has looked.
Fix
Enable debug=true and count the negativeMatches. Each rejected class still costs evaluation time — classpath reflection checks, property resolution calls, bean context queries. A typical Spring Boot application evaluating 600 auto-configuration classes where 480 are negative matches is doing significant unnecessary work per startup. Exclude known-unused configurations explicitly: @SpringBootApplication(exclude = {RabbitAutoConfiguration.class, MongoAutoConfiguration.class, KafkaAutoConfiguration.class}). Measure startup time before and after each exclusion. Consider spring.main.lazy-initialization=true for development environments to defer bean creation until first use.
×

Assuming auto-configuration always creates the bean you expect without verifying

Symptom
Bean is missing from the application context. No error at startup. Injection fails at runtime with NoSuchBeanDefinitionException or NullPointerException under production traffic. The absence is discovered through user-facing errors rather than monitoring or startup validation.
Fix
Add startup assertions for every bean that is critical to your application's function. Use an ApplicationRunner that calls context.getBeanNamesForType(CriticalType.class) and throws IllegalStateException with a meaningful message if the result is empty. This converts a silent runtime failure into a loud startup failure with an actionable error message. Then check the Conditions Evaluation Report to understand why the bean was not created — missing classpath dependency, property condition not met, or an existing bean that triggered @ConditionalOnMissingBean to skip the default.
×

Not understanding that @ConditionalOnClass silently skips configuration when the class is missing from the classpath

Symptom
Application starts without errors but a critical feature — database connectivity, message queue integration, security configuration — is completely absent. Health checks show the component as missing from the response, not as DOWN. The first indication something is wrong is a runtime exception or a user complaint, not a monitoring alert.
Fix
Declare all runtime-critical dependencies explicitly in your build file with pinned versions. Never rely on transitive resolution for libraries that auto-configuration depends on — HikariCP, Jackson, Hibernate, Lettuce, and others are commonly pulled in transitively and can be evicted by dependency resolution strategies without any build warning. Run ./gradlew dependencies or mvn dependency:tree and verify critical libraries appear in the resolved tree before every release. Add the startup assertion pattern described above to make the failure loud and immediate.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the internal working of @SpringBootApplication. Which three anno...
Q02SENIOR
What is the difference between @ConditionalOnClass and @ConditionalOnBea...
Q03SENIOR
How does Spring Boot know which auto-configuration classes to load? Walk...
Q04SENIOR
If I want to disable a specific auto-configuration like RabbitMQ without...
Q05SENIOR
Why should the @SpringBootApplication class be in the root package and w...
Q01 of 05SENIOR

Explain the internal working of @SpringBootApplication. Which three annotations does it consolidate and what does each one actually do?

ANSWER
@SpringBootApplication is a composed meta-annotation that consolidates three annotations into one: (1) @Configuration — marks the class as a source of @Bean definitions, enabling Java-based Spring configuration. Beans defined in this class are registered in the ApplicationContext directly. (2) @EnableAutoConfiguration — triggers the auto-configuration mechanism. Spring Boot uses ImportCandidates (in 3.x) or SpringFactoriesLoader (in 2.x) to scan all JARs on the classpath for the imports or spring.factories file, loads every listed configuration class, evaluates their @Conditional annotations against the current classpath and context state, and executes only those where all conditions pass. (3) @ComponentScan — instructs Spring to scan the package where this annotated class resides and all sub-packages for stereotype annotations: @Component, @Service, @Repository, @Controller, @RestController. Classes found are registered as beans automatically. This is why the main application class should always be in the root package of your application — placing it deeper in the package hierarchy causes @ComponentScan to miss sibling or parent packages.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between auto-configuration and component scanning?
02
How do I see which auto-configurations are active in my running application without restarting?
03
Can I create my own auto-configuration for a shared library that my team maintains?
04
What happens if two auto-configuration classes define beans of the same type and both pass their conditions?
05
How does the auto-configuration discovery mechanism differ between Spring Boot 2.x and Spring Boot 3.x?
🔥

That's Spring Boot. Mark it forged?

3 min read · try the examples if you haven't

Previous
Spring Boot Project Structure
3 / 15 · Spring Boot
Next
Building a REST API with Spring Boot