Understanding @Configuration vs @Component in Spring: Bean Creation and Proxies
This article explains the distinct roles of Spring's @Configuration and @Component annotations, demonstrates their behavior through code examples, compares bean initialization and proxy generation, and reveals how Spring's ConfigurationClassPostProcessor and related enhancers ensure singleton beans across method calls.
Environment: Spring 6.1.2
1. Introduction
@Configuration and @Component have different purposes and usage scenarios in the Spring framework.
@Component
@Component is a generic Spring annotation that marks a class as a component. It tells Spring to treat the class as a regular bean, to be discovered during class‑path scanning, instantiated, and registered in the container.
@Configuration
@Configuration marks a class as a configuration class. Unlike ordinary components, it primarily contains @Bean methods that define bean creation. During startup, Spring invokes these @Bean methods and registers the returned objects as beans, allowing fine‑grained control over dependency injection and component assembly.
2. Differences
2.1 Environment Preparation
public class PersonDAO {}
public class PersonService {}
public class AppConfig {
@Bean
public PersonDAO personDAO() {
return new PersonDAO();
}
@Bean
public PersonService personService() {
return new PersonService();
}
}The above classes are used for testing.
2.2 Difference 1 – Using @Configuration
@Configuration
public class AppConfig {
// ...
}
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class)) {
System.out.println(context.getBean(AppConfig.class));
System.out.println(context.getBean(PersonDAO.class));
}Output:
com.pack.AppConfig$$SpringCGLIB$$0@17f7cd29
com.pack.PersonDAO@7d8704efAppConfig is a proxy class.
2.2 Difference 1 – Using @Component
@Component
static class AppConfig {
// ...
}Output:
com.pack.AppConfig@659a969b
com.pack.PersonDAO@76908cc0AppConfig is a regular class, not a proxy.
2.3 Difference 2 – Inter‑@Bean Method Calls
When @Bean methods call each other inside a @Configuration class, Spring ensures the same singleton instance is returned.
@Configuration
static class AppConfig {
@Bean
PersonDAO personDAO() {
PersonService ps = personService();
System.out.printf("In @Bean method get PersonService: %s%n", ps);
return new PersonDAO();
}
@Bean
PersonService personService() {
return new PersonService();
}
}
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.printf("Get PersonService: %s%n", context.getBean(PersonService.class));Output:
In @Bean method get PersonService: com.pack.PersonService@2d1ef81a
Get PersonService: com.pack.PersonService@2d1ef81aThe same PersonService instance is returned, guaranteeing singleton behavior.
When the same class is annotated with @Component instead of @Configuration, the @Bean methods are not proxied, leading to different instances:
In @Bean method get PersonService: com.pack.PersonService@3901d134
Get PersonService: com.pack.PersonService@4c9f8c13This can cause multiple bean instances, potentially breaking transaction management or other container‑level features.
2.4 Changing @Configuration Proxy Mode
Setting proxyBeanMethods = false disables CGLIB proxying for @Bean methods.
@Configuration(proxyBeanMethods = false)
static class AppConfig {}Running the previous example with this setting yields different instances, similar to the @Component case.
3. @Configuration Internals
The annotation is processed by ConfigurationClassPostProcessor , a BeanFactoryPostProcessor that scans for @Configuration candidates and enhances them.
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
// 1. During container initialization, process configuration bean definitions
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
// 2. Enhance bean definitions to create CGLIB proxies
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
private void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
// Iterate over bean definitions, identify full configuration classes, and replace them with enhanced subclasses
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
Class<?> configClass = entry.getValue().getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
entry.getValue().setBeanClass(enhancedClass);
}
}
}
}The ConfigurationClassEnhancer creates a CGLIB subclass and registers three callbacks, the first of which is BeanMethodInterceptor. This interceptor ensures that each call to an @Bean method retrieves the bean from the container, preserving singleton semantics.
Thus, the proxy mechanism behind @Configuration guarantees that inter‑method calls within the configuration class always return the same bean instance.
In summary, both @Configuration and @Component can contain @Bean methods, but only @Configuration classes are proxied by Spring, which enforces singleton behavior across @Bean method invocations.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
