Understanding the Difference Between @Configuration and @Component in Spring

This article explains how Spring treats @Component and @Configuration annotations, distinguishes their LITE and FULL modes, demonstrates the proxy behavior with concrete code examples, and details the role of ConfigurationClassPostProcessor and CGLIB enhancement in implementing FULL configuration.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Understanding the Difference Between @Configuration and @Component in Spring

1. Background

With the rise of Spring Boot, annotation‑based configuration has become popular, replacing cumbersome XML configuration. Spring defines several configuration annotations such as @Component, @Configuration, @Bean, and @Import. Although their functions differ, Spring processes all of them as configuration annotations.

2. @Component and @Configuration Usage

2.1 Annotation Definitions

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
    String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(annotation = Component.class)
    String value() default "";
    boolean proxyBeanMethods() default true;
}

From the definitions, @Configuration is essentially a specialized @Component, so @ComponentScan can detect @Configuration classes.

2.2 Annotation Usage

Both annotations can mark a class as a configuration class:

@Configuration
public class AppConfig {
}

@Component
public class AppConfig {
}

Spring classifies configuration classes into two categories: LITE mode and FULL mode. @Component corresponds to LITE mode, while @Configuration corresponds to FULL mode.

LITE mode (@Component) example:

@Component
public class AppConfig {
    @Bean
    public Foo foo() {
        System.out.println("foo() invoked...");
        Foo foo = new Foo();
        System.out.println("foo() hashcode: " + foo.hashCode());
        return foo;
    }

    @Bean
    public Eoo eoo() {
        System.out.println("eoo() invoked...");
        Foo foo = foo();
        System.out.println("eoo() foo hashcode: " + foo.hashCode());
        return new Eoo();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
            new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

Execution result:

foo() invoked...
foo() hashcode: 815992954
eoo() invoked...
foo() invoked...
foo() hashcode: 868737467
eoo() foo hashcode: 868737467

Both foo() calls create separate instances.

FULL mode (@Configuration) example (same class annotated with @Configuration):

foo() invoked...
foo() hashcode: 849373393
eoo() invoked...
eoo() foo hashcode: 849373393

Here foo() is invoked only once, and the same instance is reused, demonstrating that @Configuration triggers proxying.

The difference stems from Spring’s proxy mechanism: when a class is marked as FULL configuration, Spring creates a CGLIB proxy so that @Bean methods are intercepted and bean retrieval goes through the container, ensuring singleton semantics.

3. How Spring Implements FULL Configuration

3.1 ConfigurationClassPostProcessor

Spring uses ConfigurationClassPostProcessor, a BeanDefinitionRegistryPostProcessor, to process configuration annotations during container refresh.

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {}

It is registered early via AnnotationConfigUtils.registerAnnotationConfigProcessors when the application context is created.

3.2 Instantiation Timing

The post‑processor is added as a BeanDefinition during context initialization, then its postProcessBeanDefinitionRegistry and postProcessBeanFactory methods are invoked during the refresh() phase.

AbstractApplicationContext
  -> refresh()
    -> invokeBeanFactoryPostProcessors(beanFactory);
       -> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(...);

The first method parses annotations such as @Component, @Bean, @Configuration, and registers corresponding BeanDefinition objects. The second method enhances FULL configuration classes with CGLIB proxies.

3.3 Determining LITE vs FULL

During parsing, checkConfigurationClassCandidate examines the proxyBeanMethods attribute of @Configuration. If it is true (default), the bean definition receives the attribute CONFIGURATION_CLASS_FULL; otherwise it is marked as LITE.

if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
} else if (config != null || isConfigurationCandidate(metadata)) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}

3.4 Enhancing FULL Configuration Classes

The enhanceConfigurationClasses method iterates over bean definitions, identifies those marked as FULL, and replaces their class with a CGLIB‑generated subclass.

for (String beanName : beanFactory.getBeanDefinitionNames()) {
    BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
    if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
        Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
        Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
        if (configClass != enhancedClass) {
            beanDef.setBeanClass(enhancedClass);
        }
    }
}

The generated proxy intercepts @Bean method calls via MethodInterceptor and BeanMethodInterceptor, ensuring that calls go through the container and return the same singleton instance.

4. Summary

@Component represents LITE configuration: the class is not proxied, and @Bean methods behave like ordinary Java methods, creating new instances on each call.

@Configuration represents FULL configuration: the class is proxied (CGLIB), and @Bean methods are intercepted so that the container returns a single shared instance.

In short, every @Bean method inside a @Configuration class is dynamically proxied, so the method always returns the same bean instance.
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.

JavaConfigurationSpringComponentspringbootDependencyInjection
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

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.