ConfigurationClassParser

2024. 10. 18. 12:14Spring Boot/Spring Boot Auto Configuration

ConfigurationClassParser는 Spring 프레임워크의 핵심 클래스 중 하나로, 애플리케이션의 설정 클래스를 파싱하고 분석하여 Spring 컨테이너에 빈 정의를 등록하는 역할을 합니다. 주로 @Configuration, @Component, @ComponentScan, @Import 등과 같은 어노테이션을 처리하고, 설정 정보를 기반으로 빈을 생성할 수 있도록 설정 클래스의 메타데이터를 추출합니다. 이 클래스는 Spring 컨테이너에서 Java 기반 설정을 사용하는 애플리케이션의 설정 과정을 관리하는 매우 중요한 컴포넌트입니다.

1. ConfigurationClassParser의 주요 역할

ConfigurationClassParser는 Spring이 Java 기반 설정 클래스를 읽고 빈 정의를 처리하는 핵심 프로세스에서 사용됩니다. 그 주요 기능은 다음과 같습니다:

  • 설정 클래스 파싱: @Configuration으로 정의된 Java 설정 클래스를 파싱하여 빈 정의를 추출합니다.
  • 빈 정의 등록: 파싱된 결과를 바탕으로 Spring 컨테이너에 빈 정의를 등록하는 작업에 필요한 메타데이터를 제공합니다.
  • 어노테이션 처리: @ComponentScan, @Import, @Bean 등의 어노테이션을 처리하여, 해당 설정에 맞는 빈이나 구성 요소들을 스캔하거나 등록합니다.
  • 상속 및 중첩된 설정 처리: 설정 클래스 내에서 중첩된 설정 클래스나 상속 관계를 처리하여 일관된 설정 구조를 유지합니다.

2. ConfigurationClassParser의 주요 컴포넌트

ConfigurationClassParser는 설정 클래스를 처리하는 데 여러 가지 내부 컴포넌트를 사용합니다. 그 주요 컴포넌트는 다음과 같습니다:

  • SourceClass: 설정 클래스 또는 스캔된 클래스의 추상화된 표현입니다. 클래스 메타데이터와 어노테이션 정보를 추출하는 데 사용됩니다.
  • ProblemReporter: 설정 클래스 파싱 중에 발생할 수 있는 오류나 경고를 처리합니다. 문제가 있는 설정에 대해 보고하고, 개발자가 이를 해결할 수 있도록 돕습니다.
  • ConditionEvaluator: @Conditional 어노테이션을 처리하여 특정 조건이 만족될 때만 설정 클래스 또는 빈 정의가 활성화되도록 합니다.
  • ImportStack: 설정 클래스가 다른 클래스를 @Import할 때 순환 참조 문제를 방지하기 위해 사용됩니다. @Import 어노테이션이 계층적으로 깊게 중첩될 때 이를 처리하는 역할을 합니다.

3. ConfigurationClassParser의 파싱 과정

3.1 설정 클래스 검색 및 초기화

Spring의 설정 클래스는 보통 @Configuration, @Component, @Import 등의 어노테이션을 사용해 정의됩니다. ConfigurationClassParser는 이러한 어노테이션을 기반으로 설정 클래스를 파싱하여, 빈 정의를 추출하거나, 추가적인 설정 클래스들을 찾아냅니다.

ConfigurationClassParser가 설정 클래스를 처리하는 주요 단계를 설명하면 다음과 같습니다:

  1. 설정 클래스의 파싱 시작: 설정 클래스를 입력으로 받아 파싱을 시작합니다. 이 과정은 보통 ConfigurationClassPostProcessor에 의해 호출되며, 설정 클래스가 여러 개일 경우 반복적으로 실행됩니다.
  2. 메타데이터 추출: 설정 클래스에서 @Bean, @Import, @ComponentScan, @PropertySource와 같은 어노테이션을 처리하여 메타데이터를 추출합니다. 이 메타데이터는 Spring 컨테이너가 해당 클래스와 빈을 어떻게 처리해야 할지를 결정하는 데 중요한 정보를 제공합니다.

3.2 어노테이션 처리

ConfigurationClassParser는 설정 클래스에서 다양한 어노테이션을 처리합니다. 각 어노테이션은 설정 클래스의 동작을 변경하거나, 새로운 빈을 등록하거나, 스캔을 통해 다른 클래스를 탐색하는 등의 역할을 합니다.

  • @Configuration: 해당 클래스를 Spring 설정 클래스로 인식하고, 내부에 정의된 @Bean 메서드를 빈으로 등록합니다.
  • @Bean: 설정 클래스 내부의 메서드를 호출하여 반환된 객체를 Spring 컨테이너에 빈으로 등록합니다.
  • @ComponentScan: 해당 패키지와 그 하위 패키지에서 @Component, @Service, @Repository 등으로 마킹된 클래스를 스캔하여 빈으로 등록합니다.
  • @Import: 다른 설정 클래스나 ImportSelector, ImportBeanDefinitionRegistrar 인터페이스를 구현한 클래스를 임포트하여 빈 등록 과정에 추가합니다.
  • @Conditional: 특정 조건이 만족되는 경우에만 설정 클래스 또는 빈이 등록되도록 합니다.
예시
@Configuration
@ComponentScan("com.example")
@Import(AdditionalConfig.class)
public class AppConfig {

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

위의 예제에서 ConfigurationClassParser@Configuration, @ComponentScan, @Import, @Bean을 처리하여 다음과 같은 작업을 수행합니다:

  • @ComponentScan을 처리하여 com.example 패키지에서 @Component가 붙은 클래스를 검색합니다.
  • @Import를 처리하여 AdditionalConfig.class를 추가적인 설정 클래스로 파싱합니다.
  • @Bean 메서드를 파싱하여 MyService 타입의 빈을 Spring 컨테이너에 등록합니다.

3.3 상속 및 중첩 클래스 처리

설정 클래스는 다른 설정 클래스를 상속할 수 있으며, 설정 클래스 내부에 중첩된 설정 클래스를 가질 수도 있습니다. ConfigurationClassParser는 이러한 구조를 파싱하고 처리하여 중첩된 설정 클래스와 상속된 설정 클래스 모두를 올바르게 처리합니다.

  • 상속: 설정 클래스가 다른 설정 클래스를 상속하는 경우, 부모 클래스의 @Bean, @Import, @ComponentScan 등의 어노테이션도 파싱합니다.
  • 중첩 클래스: 설정 클래스 내부에 중첩된 클래스가 있을 경우, 이 중첩 클래스에 붙은 설정 어노테이션도 파싱하여 필요한 빈을 등록하거나 구성 정보를 처리합니다.

3.4 @Import 및 동적 빈 등록

@Import는 설정 클래스에 다른 클래스를 추가하여 빈을 등록할 수 있는 기능입니다. ConfigurationClassParser@Import로 임포트된 클래스를 파싱하고, 해당 클래스에서 @Configuration, @Bean, @ComponentScan 등의 어노테이션을 처리하여 빈을 등록하거나 추가적인 설정을 적용합니다.

특히 ImportSelectorImportBeanDefinitionRegistrar는 매우 동적인 방식으로 빈을 등록할 수 있도록 합니다. ImportSelector는 임의의 클래스 이름 목록을 반환하여 이들을 빈으로 등록하게 하며, ImportBeanDefinitionRegistrarBeanDefinition을 직접 등록하는 방법을 제공합니다.

@Import(MyImportSelector.class)
public class AppConfig {
    // ...
}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.OtherConfig"};
    }
}

위의 코드에서 MyImportSelectorOtherConfig 클래스를 설정에 추가합니다. ConfigurationClassParser는 이를 처리하여 OtherConfig도 설정 클래스처럼 파싱하게 됩니다.

3.5 순환 참조 방지

설정 클래스 간에 @Import가 순환 참조될 수 있는 가능성이 있습니다. 이를 방지하기 위해 ConfigurationClassParserImportStack을 사용하여 @Import 어노테이션을 추적하고, 순환 참조가 발생하면 이를 방지하는 로직을 적용합니다.

4. 파싱 후 결과

ConfigurationClassParser는 파싱된 결과를 바탕으로 설정 클래스의 메타데이터를 생성하고, Spring의 ConfigurationClassBeanDefinitionReader에 전달하여 빈 정의를 컨테이너에 등록합니다. 이 과정에서 빈 메타데이터가 실제 Spring의 BeanFactory에 등록되고, 이후 애플리케이션 실행 시점에 빈이 생성됩니다.

5. 결론

ConfigurationClassParser는 Spring의 Java 기반 설정 처리 과정에서 매우 중요한 역할을 합니다. 이 클래스는 설정 클래스에서 다양한 어노테이션을 파싱하고, 그에 따라 Spring 컨테이너에 빈 정의를 등록하거나 다른 설정 클래스를 추가로 파싱하는 등의 역할을 수행합니다. 특히 어노테이션 처리, 상속 및 중첩 클래스 지원, 조건부 빈 등록, 동적 빈 추가 등 다양한 기능을 통해 Spring 애플리케이션 설정을 유연하고 확장 가능하게 만듭니다.