What is a Configuration class
A @Configuration class is a Spring configuration component containing one or more @Bean methods that act as a programmatic factory and source of BeanDefinitions for the Spring I...
🟢 Junior Level
A @Configuration class is a Spring configuration component containing one or more @Bean methods that act as a programmatic factory and source of BeanDefinitions for the Spring IoC container.
package com.example.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// Programmatic customization of a third-party class:
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
When to Use @Configuration Over @Component
While stereotype annotations (@Component, @Service, @Repository) are placed directly over your own domain classes, @Configuration with @Bean is essential in two scenarios:
- Third-Party Libraries: You cannot add
@Componentto classes from third-party JARs (e.g. Jackson’sObjectMapper, AWS SDK’sAmazonS3Client, orHikariDataSource). - Complex Programmatic Initialization: When bean construction requires multi-step builder patterns, environment property lookups, conditional logic, or invoking specific factory methods.
🟡 Middle Level
Resolving Inter-Bean Dependencies
When one @Bean method depends on another bean defined within the same configuration class, there are two distinct approaches:
@Configuration
public class SecurityAppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Approach 1: Direct Method Call (Inter-Bean Reference)
@Bean
public UserService userService() {
return new UserService(passwordEncoder()); // CGLIB intercepts this call!
}
// Approach 2: Method Parameter Injection (Recommended Best Practice)
@Bean
public AuthService authService(PasswordEncoder passwordEncoder) {
return new AuthService(passwordEncoder); // Spring automatically resolves and injects the singleton bean
}
}
Full Mode vs Lite Mode (proxyBeanMethods)
Spring supports two operational modes for configuration classes, governed by the proxyBeanMethods attribute:
| Criterion | Full Mode (proxyBeanMethods = true) |
Lite Mode (proxyBeanMethods = false) |
|---|---|---|
| Declaration | @Configuration (Default) |
@Configuration(proxyBeanMethods = false) or @Bean inside @Component |
| Proxy Generation | Wrapped in a CGLIB dynamic subclass | No proxy generated (instantiated as a standard POJO) |
Inter-Bean Calls b(a()) |
Intercepted by CGLIB; returns the cached singleton from container | Direct Java method invocation; creates a redundant duplicate instance |
| Java Constraints | Class and methods cannot be final |
Class and methods can be declared final |
| Startup Overhead | Slower startup; incurs ASM bytecode generation & Metaspace cost | 30–50% faster startup; lower memory footprint |
| GraalVM Native Image | Requires reflection hints for CGLIB subclass | Fully Native-Image friendly out of the box |
🔴 Senior Level
Under the Hood: ConfigurationClassPostProcessor & CGLIB Interception
During container startup, Spring’s ConfigurationClassPostProcessor (a BeanDefinitionRegistryPostProcessor) processes all @Configuration classes:
- If
proxyBeanMethods == true, Spring delegates toConfigurationClassEnhancer. - Using CGLIB, Spring generates a runtime subclass:
SecurityAppConfig$$SpringCGLIB$$0 extends SecurityAppConfig. - It registers a specialized interceptor named
BeanMethodInterceptor:
// Conceptual logic inside CGLIB BeanMethodInterceptor in Spring Framework:
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String beanName = BeanAnnotationHelper.determineBeanNameFor(method);
// 1. If the container is currently instantiating this bean definition via factory:
if (isCurrentlyInvokedFactoryMethod(method)) {
return proxy.invokeSuper(obj, args); // Executes original @Bean method body
}
// 2. If invoked from another @Bean method (inter-bean call):
// Reroute the call directly to the BeanFactory cache!
return this.beanFactory.getBean(beanName);
}
Inter-Bean Call Execution Flow:
UserService userService() starts execution
│
▼
Calls passwordEncoder() directly
│
▼
Intercepted by CGLIB BeanMethodInterceptor
│
▼
Queries Level 1 Cache: beanFactory.getBean("passwordEncoder")
│
▼
Returns already existing BCryptPasswordEncoder singleton!
(No second BCryptPasswordEncoder instance is allocated)
The Production Disaster of Lite Mode: Accidental Duplicate Singletons
If a developer disables CGLIB proxying (proxyBeanMethods = false) but continues to use direct inter-bean method calls:
@Configuration(proxyBeanMethods = false) // Lite Mode
public class DangerousConfig {
@Bean
public ConnectionPool pool() {
return new ConnectionPool(); // Instance 1
}
@Bean
public OrderRepository orderRepository() {
// ❌ DEFECT: Directly calling pool() invokes standard Java method!
return new OrderRepository(pool()); // Creates Instance 2 of ConnectionPool!
}
@Bean
public UserRepository userRepository() {
// ❌ DEFECT: Directly calling pool() invokes standard Java method again!
return new UserRepository(pool()); // Creates Instance 3 of ConnectionPool!
}
}
Consequences: Instead of sharing a single database connection pool, the application instantiates three completely independent connection pools. This exhausts database connections, leaks memory, and causes severe production outages under traffic.
The Safe Pattern in Lite Mode:
Always pass dependencies as method parameters:
@Configuration(proxyBeanMethods = false) // ✅ Safe and performant
public class SafeConfig {
@Bean
public ConnectionPool pool() {
return new ConnectionPool();
}
@Bean
public OrderRepository orderRepository(ConnectionPool pool) { // Spring injects singleton pool
return new OrderRepository(pool);
}
@Bean
public UserRepository userRepository(ConnectionPool pool) { // Spring injects the same singleton pool
return new UserRepository(pool);
}
}
@Bean Methods Inside Ordinary @Component Classes
Declaring a @Bean method inside a standard @Component, @Service, or @Controller class is officially supported, but it executes strictly in Lite Mode.
Spring never creates a CGLIB subclass for a @Component. Inter-bean method calls within @Component classes will always invoke raw Java methods, creating duplicate unmanaged instances.
4 Tricky Questions
1. What is the fundamental difference between Full Mode and Lite Mode in Spring @Configuration classes?
Answer:
- Full Mode (
proxyBeanMethods = true, default): Spring wraps the configuration class in a CGLIB dynamic subclass. Direct calls from one@Beanmethod to another are intercepted byBeanMethodInterceptor, which retrieves the existing singleton bean from theBeanFactory(beanFactory.getBean(beanName)), guaranteeing singleton semantics. - Lite Mode (
proxyBeanMethods = false): Spring treats the configuration class as a standard plain Java POJO without CGLIB enhancement. Direct calls between@Beanmethods execute as normal Java method invocations, creating brand-new, duplicate instances that bypass container lifecycle management.
2. Why can a @Configuration class in Full Mode never be declared as final?
Answer:
In Full Mode, Spring relies on CGLIB to generate a subclass (MyConfig$$SpringCGLIB$$0 extends MyConfig) that overrides all @Bean methods with interceptor callbacks.
Under JVM specifications and Java language rules, a class marked final cannot be extended by any subclass, and a method marked final cannot be overridden. If Spring attempts to process a final @Configuration class with proxyBeanMethods = true, CGLIB subclass generation fails immediately, throwing an IllegalArgumentException: Cannot subclass final class and crashing application startup.
3. What happens if a developer declares @Bean methods inside a standard @Component or @Service class?
Answer:
Spring successfully processes the @Bean methods and registers the returned instances in the ApplicationContext. However, Spring processes @Bean methods inside @Component, @Service, or @Repository classes strictly in Lite Mode.
Spring does not generate a CGLIB proxy for standard @Component classes. If one @Bean method calls another @Bean method directly inside that @Component, it will execute a raw Java constructor call, generating duplicate, unmanaged objects that are not tracked as Spring singletons.
4. Why did Spring Boot 2.2+ introduce proxyBeanMethods = false, and how does it prevent connection pool duplication?
Answer:
proxyBeanMethods = false was introduced to optimize startup performance and memory consumption, and to ensure native compatibility with GraalVM Ahead-Of-Time (AOT) compilation:
- Generating CGLIB bytecode subclasses at runtime increases startup latency and consumes Metaspace memory.
- GraalVM Native Image compilation does not support dynamic runtime bytecode generation.
To prevent connection pool duplication when using proxyBeanMethods = false, developers must never invoke @Bean methods directly. Instead, all inter-bean dependencies must be declared as parameters of the @Bean method (e.g. @Bean public OrderRepo repo(ConnectionPool pool)). Spring resolves parameters directly from the container’s singleton cache, guaranteeing that only one connection pool exists.
🎯 Interview Cheat Sheet
30-Second Elevator Pitch
A
@Configurationclass is a Spring component containing@Beanfactory methods that define, configure, and register beans programmatically. It is primarily used for integrating third-party libraries and complex instantiation logic. It operates in two modes:
- Full Mode (
proxyBeanMethods = true, default): Enhanced via CGLIB subclassing. Inter-bean method calls are intercepted to return the cached singleton fromBeanFactory. Class and methods cannot befinal.- Lite Mode (
proxyBeanMethods = false): No CGLIB proxy; faster startup and lower memory footprint. Inter-bean calls execute raw Java constructors (creating duplicates), so dependencies must be injected via method parameters.
Full vs Lite Mode Comparison
| Feature | Full Mode (proxyBeanMethods = true) |
Lite Mode (proxyBeanMethods = false) |
|---|---|---|
| Proxy Type | CGLIB Subclass Proxy | None (Plain POJO) |
| Inter-Bean Direct Calls | Returns cached singleton bean | Allocates new duplicate instance |
| Class Modifier | Cannot be final |
Can be final |
| GraalVM Native Image | Requires reflection hints | Natively supported out of the box |
| Dependency Injection | Method calls or parameters | Method parameters only |
Red Flags (DO NOT Say)
- ❌ “In Lite Mode,
@Beanmethods no longer register beans in the context.” (They register beans normally; only CGLIB interception of inter-bean calls is disabled). - ❌ “Calling an inter-bean method in Full Mode always runs the method body again.” (The CGLIB interceptor intercepts the call and returns the cached singleton from
BeanFactory). - ❌ “You can safely mark
@Configurationclasses asfinalin Full Mode.” (CGLIB cannot subclass a final class; startup will fail). - ❌ “Lite Mode prevents passing dependencies between
@Beanmethods.” (Dependencies are passed cleanly via method arguments:@Bean public B b(A a)).