Backend Development 9 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Understanding @Configuration vs @Component in Spring: Bean Creation and Proxies

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

<code>public class PersonDAO {}

public class PersonService {}

public class AppConfig {
  @Bean
  public PersonDAO personDAO() {
    return new PersonDAO();
  }

  @Bean
  public PersonService personService() {
    return new PersonService();
  }
}
</code>

The above classes are used for testing.

2.2 Difference 1 – Using @Configuration

<code>@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));
}
</code>

Output:

<code>com.pack.AppConfig$$SpringCGLIB$$0@17f7cd29
com.pack.PersonDAO@7d8704ef
</code>

AppConfig is a proxy class.

2.2 Difference 1 – Using @Component

<code>@Component
static class AppConfig {
  // ...
}
</code>

Output:

<code>com.pack.AppConfig@659a969b
com.pack.PersonDAO@76908cc0
</code>

AppConfig 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.

<code>@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));
</code>

Output:

<code>In @Bean method get PersonService: com.pack.PersonService@2d1ef81a
Get PersonService: com.pack.PersonService@2d1ef81a
</code>

The 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:

<code>In @Bean method get PersonService: com.pack.PersonService@3901d134
Get PersonService: com.pack.PersonService@4c9f8c13
</code>

This 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.

<code>@Configuration(proxyBeanMethods = false)
static class AppConfig {}
</code>

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.

<code>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);
      }
    }
  }
}
</code>

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.

JavaconfigurationSpringcomponentAnnotationsDependency Injection
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.