@ComponentScan vs @Import, precedence between XML, Java config and @Value, mixing XML and Java config, @Configuration vs @Component (full vs lite mode), @Bean lifecycle, duplicate @Bean types, overriding library beans, environment-aware configuration, multiple scan packages, @Bean outside @Configuration, how auto-configuration works (AutoConfiguration.imports, conditions, ordering), enabling/disabling it, conditional beans, starters that never clash with Boot, a cloud messaging starter design, @TestConfiguration vs @SpringBootTest, test profiles, multiple active profiles, YAML and encrypted properties, Docker configuration per environment, a production misconfiguration story, and multi-module Spring Boot architecture.
Published September 25, 2026
Configuration questions check whether you can predict which bean and which property value win, and whether you can build platform starters that behave well. Anchor your answers in conditions, ordering and property-source precedence.
@ComponentScan and @Import?Short answer:
@ComponentScan discovers classes annotated with @Component (and its stereotypes) by scanning packages on the classpath. It's implicit and broad.@Import explicitly registers specific classes: @Configuration classes, **ImportSelector**s (which choose the classes to import programmatically) or **ImportBeanDefinitionRegistrar**s (which register definitions directly). No scanning happens.Boot's @SpringBootApplication scans the application's package. Auto-configuration and @Enable* annotations use imports (for example, @EnableScheduling imports a configuration). Use @Import for libraries and modules, so consumers don't depend on package scanning.
Learn it in depth → Component Scanning & Configuration
@Value?Short answer: They configure different things, so separate bean definitions from property values:
spring.main.allow-bean-definition-overriding=false), so startup fails. In plain Spring, the registration order depends on how the configurations are imported (for example, @ImportResource XML processed after the class's own @Beans).@Value("${prop}") doesn't have "precedence" over XML or Java config. It reads from the Environment, whose property sources have a defined order (command-line arguments > system properties > environment variables > external and internal config files > defaults).<property> values are applied after annotation injection, so they override field values set by @Autowired or @Value on the same bean.Avoid designs that depend on overriding. Use conditions, profiles or @Primary.
Short answer:
@ImportResource("classpath:legacy-context.xml") on a @Configuration class.<context:annotation-config/> plus <bean class="com.acme.AppConfig"/>, or <context:component-scan> in the XML.It's typical during migration of legacy applications. Move bean definitions to Java config incrementally, and keep the XML only for rarely changed or third-party definitions. Spring Boot still supports @ImportResource.
@Configuration differ from a regular @Component class that declares @Bean methods?Short answer:
@Configuration (full mode): the class is CGLIB-proxied. Calls from one @Bean method to another are intercepted, and return the container singleton:@Configuration
class DbConfig {
@Bean DataSource dataSource() { return new HikariDataSource(); }
@Bean JdbcTemplate jdbc() { return new JdbcTemplate(dataSource()); } // same singleton DataSource, through the proxy
}
@Component or @Configuration(proxyBeanMethods = false) (lite mode): no proxy, so dataSource() is a plain Java call that creates a new HikariDataSource: a second pool, and a resource leak. Lite mode starts faster, and works with GraalVM native images. Boot's own auto-configurations use it, and inject dependencies as method parameters instead.@Bean lifecycle, and how does method-level configuration differ from field injection?Short answer:
Lifecycle: the container calls the @Bean factory method (resolving its parameters as dependencies), then applies the normal lifecycle: population → aware callbacks → BeanPostProcessor before-init → @PostConstruct/afterPropertiesSet/initMethod → after-init (proxies) → ... → on shutdown, @PreDestroy/destroyMethod. destroyMethod is inferred by default (a public close() or shutdown() method is called automatically).
Compared with field injection:
@Bean methods receive dependencies as parameters, so the construction is explicit and testable;Field injection hides dependencies, and only works on classes Spring instantiates itself.
@Bean methods return the same type, without any qualifier?Short answer: Both beans are registered, under their method names. Injecting by type then fails with NoUniqueBeanDefinitionException, unless:
@Primary;@Qualifier("name"), or a parameter or field name that matches a bean name;List<T>/Map<String,T> of all of them.If the two methods have the same name (for example, in different configuration classes), Boot fails at startup, because overriding is disabled.
Short answer: It depends on how the library defines it:
@ConditionalOnMissingBean (most Boot starters): define your own bean of that type, and the auto-configuration backs off. This is the intended mechanism.*Customizer beans (Jackson2ObjectMapperBuilderCustomizer, RestClientCustomizer, WebServerFactoryCustomizer), so you can tweak the default instead of replacing it.spring.autoconfigure.exclude=..., or @SpringBootApplication(exclude = ...), then define your own.spring.main.allow-bean-definition-overriding=true. It's a blunt instrument, and order-dependent, so avoid it.@Primary on your bean, if both can coexist.BeanDefinitionRegistryPostProcessor, to remove or replace definitions (a last resort).Short answer:
application-{profile}.yml, @Profile on beans, and spring.profiles.active, or profile groups (spring.profiles.group.prod=cloud,metrics).spring.config.import (configuration trees for Kubernetes secrets, Config Server, Vault, AWS Parameter Store).@ConditionalOnProperty, @ConditionalOnCloudPlatform, @ConditionalOnClass, custom @Conditionals.@ConfigurationProperties records, with @Validated, so every environment fails fast on bad values.@ComponentScan lists several packages?Short answer: Spring scans all of them (@ComponentScan(basePackages = {"com.a", "com.b"}), or the type-safe basePackageClasses), and registers every candidate it finds. Overlapping packages are fine: each class is registered once, since definitions are keyed by bean name. But watch for:
DefaultClient classes), which is a conflict;@SpringBootApplication default: declaring your own @ComponentScan replaces the default scanning of the main class's package and its auto-configuration exclusion filters, which is a common source of odd behaviour.Put the main class in the root package, so the default scan covers everything, and use filters (includeFilters/excludeFilters) when needed.
@Bean in a class that isn't a @Configuration? What are the consequences?Short answer: Yes. It's "lite mode". @Bean methods in a @Component, or even in a plain class registered as a bean, are processed. The consequence is that inter-bean method calls aren't intercepted, so calling another @Bean method directly creates a new instance, bypassing the container: duplicate singletons, lost lifecycle callbacks and no AOP. Fix: inject the dependencies through method parameters, and never call @Bean methods directly in lite mode.
Short answer:
@SpringBootApplication includes @EnableAutoConfiguration, which imports AutoConfigurationImportSelector.META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports, in every JAR (Boot 2.7+). Previously, it read spring.factories.exclude attributes, and spring.autoconfigure.exclude), and filters early using the condition metadata (fast checks of @ConditionalOnClass without loading classes).@AutoConfiguration classes are ordered (@AutoConfiguration(before/after), @AutoConfigureOrder), and processed after the user's configuration. So user beans are registered first, and @ConditionalOnMissingBean backs off correctly.@ConditionalOnClass, @ConditionalOnMissingBean, @ConditionalOnProperty, @ConditionalOnWebApplication, @ConditionalOnBean, @ConditionalOnResource, @ConditionalOnCloudPlatform.--debug (the condition evaluation report) or /actuator/conditions.Learn it in depth → Auto-Configuration Mechanism
spring.factories, and of the AutoConfiguration.imports file?Short answer: They're how auto-configurations are registered, so Boot can find them in JARs without scanning:
META-INF/spring.factories: the legacy key-value file. For auto-configuration (the EnableAutoConfiguration key), it was deprecated in Boot 2.7, and no longer supported for that purpose in Boot 3. It's still used for other extension points (for example ApplicationContextInitializer, EnvironmentPostProcessor, FailureAnalyzer).META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: the current file, with one fully qualified class name per line, for @AutoConfiguration classes.Common trap: a custom starter that still registers its auto-configuration in spring.factories silently does nothing on Boot 3.
Short answer:
@SpringBootApplication (or @EnableAutoConfiguration).@SpringBootApplication(exclude = DataSourceAutoConfiguration.class), or excludeName for classes not on the classpath;spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration (a property, so it can be changed per environment).@EnableAutoConfiguration (use @Configuration + @ComponentScan), or set spring.boot.enableautoconfiguration=false.@ConditionalOnMissingBean defaults step aside.Common trap: the source says spring.autoconfigure.exclude "disables all" auto-configuration. It excludes only the listed classes.
Short answer: Registering beans only when conditions hold, evaluated at configuration time:
@Conditional(MyCondition.class) (implement Condition.matches), and @Profile (which is itself a condition).@ConditionalOnClass/OnMissingClass;@ConditionalOnBean/OnMissingBean;@ConditionalOnProperty;@ConditionalOnResource;@ConditionalOnWebApplication;@ConditionalOnExpression;@ConditionalOnJava;@ConditionalOnCloudPlatform;@ConditionalOnSingleCandidate;@ConditionalOnThreading(VIRTUAL).The caveats:
@ConditionalOnBean/OnMissingBean depend on registration order. They're reliable in auto-configurations (processed after user config), and unreliable in regular @Configuration classes.ApplicationContextRunner.Short answer:
@ConditionalOnMissingBean, so users (and Boot) can provide their own.@ConditionalOnClass: activate only when your library is present.@ConditionalOnProperty to switch it off (acme.feature.enabled=false).@AutoConfiguration(after = DataSourceAutoConfiguration.class).acme.*), never spring.*.ObjectMapper, RestClient.Builder).AutoConfiguration.imports.ApplicationContextRunner (with and without the user's beans, with the properties on and off).Short answer:
acme-messaging-spring-boot-autoconfigure (the logic), and acme-messaging-spring-boot-starter (dependencies only), plus a BOM for versions.@ConfigurationProperties("acme.messaging"): provider (SQS, Pub/Sub, Service Bus), endpoints, credentials references (never raw secrets), retry and backoff, DLQ names, concurrency, and serialisation format. Validated, with IDE metadata.MessagePublisher, and @AcmeListener or MessageHandler registration);@ConditionalOnClass or properties;SmartLifecycle.@ConditionalOnMissingBean everywhere, ApplicationContextRunner tests, Testcontainers or LocalStack integration tests, documentation, and a sample application.@TestConfiguration and @SpringBootTest differ?Short answer:
@SpringBootTest bootstraps the full application context (optionally with a real web server, webEnvironment = RANDOM_PORT), finding the @SpringBootConfiguration. It's for integration tests.
@TestConfiguration adds or overrides beans for tests only:
@Import.It's excluded from component scanning, so it doesn't leak into other tests. For example, a fixed Clock, a fake payment gateway, or a Testcontainers-backed DataSource.
Use them together: @SpringBootTest plus @Import(TestClockConfig.class). Remember that every distinct configuration creates a new cached context, which slows the test suite.
Short answer:
@Profile("test").@ActiveProfiles("test"), and use application-test.yml for test properties.@TestConfiguration plus @Import for bean overrides, and @TestPropertySource or @DynamicPropertySource (with Testcontainers) for properties. Those are more explicit than profiles, and don't risk leaking test beans into production if someone activates the "test" profile.src/main guarded only by a profile.application-<profile>.yml files are active, which one wins?Short answer: The last profile listed wins for conflicting keys. With spring.profiles.active=dev,local, the values in application-local.yml override application-dev.yml, and all profile-specific files override application.yml. Profile-specific files outside the JAR override those inside it. Environment variables and command-line arguments override all of them. Within a multi-document YAML, later documents override earlier ones for matching activation conditions. Use profile groups to define a stable order.
Short answer:
application.yml, with hierarchical keys and lists;---, with spring.config.activate.on-profile;@ConfigurationProperties records, rather than scattered @Values;.properties wins over .yml in the same location.spring.config.import=vault://), AWS Secrets Manager or Parameter Store, Azure Key Vault, Kubernetes Secrets (mounted as a configuration tree), or sealed secrets or SOPS in GitOps.{cipher}... values, decrypted server-side or client-side with a symmetric key or a key store. Jasypt (ENC(...)) is a common library alternative. The key itself must come from outside the repository (an environment variable or a KMS)./actuator/env and logs.Short answer: One immutable image, configuration injected at runtime:
SPRING_DATASOURCE_URL, APP_PAYMENTS_TIMEOUT), set through Compose, Kubernetes manifests, or Helm values per environment.spring.config.import=optional:configtree:/etc/config/) from Kubernetes ConfigMaps and Secrets.SPRING_PROFILES_ACTIVE, for environment-specific defaults that ship inside the JAR (non-secret).@Validated properties), and version configuration changes in Git (GitOps), with review.Short answer: Answer with a STAR story. A realistic example: a new release set spring.datasource.hikari.maximum-pool-size=100 per pod, while the autoscaler went to 20 pods. That's 2,000 connections against a database limited to 500. Under peak traffic, connections failed, requests timed out, and the database CPU spiked.
@ConfigurationProperties;The lesson: configuration is code. Other classic examples: timeouts set too low, a wrong profile active in production (dev configuration, or H2 used), and feature flags defaulting to on.
Short answer: It splits one application (or a platform) into Maven or Gradle modules, with explicit dependencies:
web, persistence, messaging;app module that assembles them with the Spring Boot plugin;*-api modules, for contracts.The benefits:
The practices:
Q: What is @ConfigurationProperties scanning?
A: @ConfigurationPropertiesScan (or @EnableConfigurationProperties(MyProps.class)) registers property classes as beans. Records get constructor binding automatically (Boot 3). Add the spring-boot-configuration-processor for IDE metadata.
Q: How do you see why a bean was, or wasn't, auto-configured?
A: Run with --debug, or logging.level.org.springframework.boot.autoconfigure=DEBUG, or check /actuator/conditions. The report lists positive and negative matches, with the exact condition outcomes.
Q: What is an EnvironmentPostProcessor?
A: A hook (registered in spring.factories) that customises the Environment before the context refreshes. It adds property sources, or decrypts or derives values. It's used by configuration libraries and cloud integrations.
Q: Can @Value inject lists and defaults?
A: Yes: @Value("${app.hosts:localhost}") for a default, and SpEL #{'${app.hosts}'.split(',')} for lists. @ConfigurationProperties is cleaner for structured configuration, and supports relaxed binding and validation.