ConfigurationClassPostProcessor

2024. 10. 18. 16:47Spring Boot/Spring Boot Auto Configuration

ConfigurationClassPostProcessor스프링 프레임워크에서 매우 중요한 역할을 하는 클래스 중 하나로, @Configuration을 기반으로 하는 Java 기반의 설정 클래스를 처리하고 관리하는 핵심 빈 팩토리 후처리기(BeanFactoryPostProcessor)입니다. 이 클래스는 주로 빈 정의를 읽고, 분석하며, 이를 바탕으로 빈을 등록하는 과정을 수행합니다. 특히 Spring 3.0 이후 Java 기반 설정 클래스가 도입되면서 더욱 중요한 역할을 하게 되었습니다.

1. 주요 역할

ConfigurationClassPostProcessor는 스프링에서 애노테이션 기반 설정을 처리하는 과정에서 중요한 역할을 합니다. 주요 작업은 다음과 같습니다:

  • @Configuration, @ComponentScan, @Import, @Bean 등의 애노테이션을 처리
  • 빈 정의를 등록 및 수정
  • 중복 빈 정의를 피하기 위한 CGLIB 프록시 기반의 메커니즘 제공
  • 스프링의 Java 설정 파일을 처리하고, 이를 기반으로 추가적인 빈 정의를 생성

2. 동작 원리

이 클래스는 BeanFactoryPostProcessor를 구현하고 있기 때문에, 스프링 컨테이너가 초기화되는 과정 중에 실행됩니다. 일반적으로 애플리케이션 컨텍스트가 시작될 때, 스프링은 ConfigurationClassPostProcessor를 사용하여 @Configuration이 적용된 클래스를 분석하고, 해당 설정 클래스에서 정의한 빈 메타데이터를 기반으로 빈 정의를 등록하게 됩니다.

3. 주요 메서드 및 흐름

3.1 postProcessBeanDefinitionRegistry()

이 메서드는 스프링의 초기화 과정에서 빈 정의 레지스트리(BeanDefinitionRegistry)에 접근하여, @Configuration 클래스를 찾고 처리하는 역할을 합니다.

  • @Configuration 클래스 탐색: postProcessBeanDefinitionRegistry()BeanDefinitionRegistry에서 모든 빈 정의를 검색하며, 이 중 @Configuration 애노테이션이 적용된 클래스를 찾습니다.
  • 빈 정의 추가 및 수정: 찾은 설정 클래스에 대해 @Bean 메서드와 같은 빈 정의를 분석하여, 이를 BeanDefinition으로 변환한 후 레지스트리에 등록합니다.

3.2 processConfigBeanDefinitions()

이 메서드는 실제로 @Configuration 클래스를 처리하는 주요 메서드입니다. 다음과 같은 작업을 수행합니다:

  • @Configuration 클래스 분석: processConfigBeanDefinitions()@Configuration이 적용된 클래스를 분석하여 @Bean, @ComponentScan, @Import 등을 처리합니다.
  • 빈 정의 등록: 메서드 내부에서 ConfigurationClassParser를 사용하여, Java 설정 클래스에서 정의한 @Bean 메서드 등을 분석하고 이를 기반으로 새로운 빈 정의를 레지스트리에 추가합니다.

3.3 enhanceConfigurationClasses()

이 메서드는 CGLIB 프록시를 사용하여 @Configuration 클래스를 향상시킵니다. CGLIB를 사용하는 이유는 빈 메서드 간의 상호 참조 문제를 해결하기 위해서입니다. 기본적으로 @Bean 메서드를 호출할 때 직접 호출되지 않고 프록시를 통해 호출되도록 함으로써, 싱글톤 빈 보장을 위해 동작합니다.

4. 프록시 기반 처리

스프링은 @Configuration 클래스에서 정의한 @Bean 메서드를 호출할 때 CGLIB 프록시를 통해 메서드를 호출합니다. 이는 같은 클래스 내에서 서로 다른 @Bean 메서드를 호출할 때 새로운 인스턴스를 생성하지 않고 이미 스프링 컨테이너에 등록된 빈을 반환하기 위해서입니다.

예를 들어, 같은 @Configuration 클래스에서 두 개의 @Bean 메서드가 상호 참조하는 경우, 이 프록시 메커니즘을 통해 싱글톤 컨테이너의 일관성을 유지합니다.

5. ConfigurationClassParser

ConfigurationClassPostProcessor는 내부적으로 ConfigurationClassParser를 사용하여 설정 클래스를 파싱합니다. 이 클래스는 여러 애노테이션을 분석하고 처리하여, 해당 애노테이션에 정의된 빈 정의들을 파싱 트리 형태로 구성합니다.

  • @Import 처리: ConfigurationClassParser@Import 애노테이션을 분석하여 다른 설정 클래스임포트된 빈 설정도 함께 처리합니다.
  • @ComponentScan 처리: 패키지 스캔을 통해 컴포넌트 클래스들을 찾아내어 빈으로 등록할 수 있도록 돕습니다.

6. 동작 과정 요약

  1. @Configuration 클래스 탐색: postProcessBeanDefinitionRegistry()에서 애플리케이션 컨텍스트 내에서 @Configuration이 적용된 클래스를 찾습니다.
  2. 빈 정의 분석: processConfigBeanDefinitions()에서 @Bean 메서드, @ComponentScan, @Import 등을 분석하여 빈 정의를 생성합니다.
  3. 빈 등록: 분석한 빈 정의를 BeanDefinitionRegistry에 등록합니다.
  4. 프록시 생성: enhanceConfigurationClasses()에서 CGLIB 프록시를 사용하여 @Bean 메서드 호출 시 동작 방식을 제어합니다.
  5. 완료: 빈 팩토리 후처리기를 완료하고 나면, @Bean 메서드로 정의된 빈이 스프링 컨테이너에 등록되고 관리됩니다.

7. 확장성

ConfigurationClassPostProcessor는 스프링의 확장 포인트로 사용될 수 있으며, 커스텀 BeanFactoryPostProcessor와 결합하여 사용자 정의 설정 로직을 추가할 수도 있습니다. 예를 들어, 특정 조건에 따라 추가적인 빈 정의를 등록하거나, 기존의 빈 정의를 수정할 수 있습니다.

8. 예시 코드 흐름

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

    @Bean
    public MyRepository myRepository() {
        return new MyRepositoryImpl();
    }
}
  1. ConfigurationClassPostProcessor가 실행되면서 AppConfig 클래스가 설정 클래스로 인식됩니다.
  2. AppConfig 클래스 내의 @Bean 메서드인 myService()myRepository()가 파싱되어 BeanDefinition으로 등록됩니다.
  3. 프록시 객체가 만들어져 AppConfig 클래스 내에서 빈을 호출할 때 싱글톤으로 관리되도록 보장합니다.

ConfigurationClassPostProcessor는 스프링의 애노테이션 기반 설정을 위한 핵심 처리기로서, 설정 클래스의 메타데이터를 읽고, 이를 스프링의 빈 정의로 변환하여, 스프링 컨테이너가 애플리케이션을 실행하는 데 필요한 빈을 적절하게 관리하고 주입할 수 있도록 지원합니다.