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

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@7d8704ef

AppConfig is a proxy class.

2.2 Difference 1 – Using @Component

@Component
static class AppConfig {
  // ...
}

Output:

com.pack.AppConfig@659a969b
com.pack.PersonDAO@76908cc0

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.

@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@2d1ef81a

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:

In @Bean method get PersonService: com.pack.PersonService@3901d134
Get PersonService: com.pack.PersonService@4c9f8c13

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.

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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.