[Spring Boot]

개요

Spring Boot의 bean 설정 정보는 Component Scan에 의해 등록된 bean과 AutoConfiguration에 의해 자동 등록된 bean으로 구분된다.

이 포스트에서는 일반적으로 자동 등록되는 Bean으로 AutoConfiguration의 동작 메커니즘에 대해 작성합니다.

콩의 역할과 구별

AutoConfiguration 동작 메커니즘에 대해 배우기 전에 Bean 정의와 함께 역할 및 차이점을 이해해야 합니다.

Bean은 Spring 컨테이너에 의해 생성되고 관리되는 Java 객체이며 모두 싱글톤으로 관리됩니다.

콩은 대략 다음과 같이 세 가지 유형으로 분류할 수 있습니다.

  • 애플리케이션 로직 빈
  • 애플리케이션 인프라 빈
  • 컨테이너 인프라 빈

애플리케이션 로직 빈

  • 응용 프로그램의 기능, 비즈니스 논리 및 도메인 논리를 포함하고 일반적으로 응용 프로그램을 개발할 때 개발되는 코드로 빌드되는 Bean.
  • 예: 책임자, 서비스, 저장소, …

애플리케이션 인프라 빈

  • 대부분 개발자가 직접 작성 X(일반적으로 자동 구성에 의해 추가됨)
  • Bean 구성 정보로 컨테이너에 등록된 Bean이지만 애플리케이션 로직이 아니라 애플리케이션이 실행되기 위해 필요한 기술 기반을 제공하는 Bean입니다.
  • 예) DispatcherServlet과 TomcatWebServletFactory는 기존 Spring Application에서는 Bean으로 등록되지 않았으나 Spring Boot에서는 Configuration 정보를 통해 Bean으로 등록된다.
  • 예) DataSource, JpaEntityManagerFactory, JdbcTransactionManager, …

컨테이너 인프라 빈

  • 스프링컨테이너나 스프링컨테이너는 Bean으로 기능을 확장하면서 추가로 등록하여 사용
  • 개발자가 작성한 설정 정보에 의해 생성되는 것이 아니라 Spring 자체적으로 추가됨(Application Infrastructure Bean과 다름)
  • ex) DefaultAdvisorAutoProxyCreator, 환경, 애플리케이션 컨텍스트, …

컴포넌트 스캔 대상이 아닌 빈은 @Import로 등록

@Component 주석이 달린 클래스는 @Import 주석을 통해 빈으로 등록할 수 있으며, @Configuration 주석이 달린 클래스는 정상적으로 가져올 수 있습니다.

앞에서 언급한 Application Infrastructure Bean Configuration 클래스는 Spring Boot의 AutoConfiguration 메커니즘에 의해 등록되기 전에 패키징되어야 합니다.

그리고 위와 같이 분리된 클래스는 @Import 어노테이션으로 담는다.

Spring Boot 프로젝트를 생성할 때 자동으로 생성되는 @SpringBootApplication을 자세히 보면 @EnableAutoConfiguration이 있고 이 주석을 통해 AutoConfiguration 메커니즘이 작동한다.


또한 EnableAutoConfiguration 어노테이션을 살펴보면 위에서 언급한 @Import 어노테이션을 볼 수 있는데 AutoConfigurationImportSelector 클래스를 통해 Application Infrastructure Bean을 등록하고 있음을 알 수 있다.


ImportSelector 인터페이스

AutoConfigurationImportSelector 클래스를 보면 ImportSelector 인터페이스를 구현하고 있음을 알 수 있습니다.

ImportSelector 인터페이스의 핵심 메소드는 selectImports()이며, 이 메소드가 반환하는 클래스명은 @Configuration 클래스를 찾아 설정 정보로 사용한다.


ImportSelector를 구현한 AutoConfiguraitonImportSelector의 selectImports 메소드는 다음과 같다.

메서드 이름에서 우리는 selectImports 메서드가 호출될 때 다음 프로세스가 발생한다는 결론을 내릴 수 있습니다.

  • 구성 정보에 추가할 모든 후보 구성을 로드한 후,
  • 중복 검사를 하다
  • 제외할 클래스를 제외한 후
  • 한 번 필터링
  • 자동 구성 진행

@Configuration 클래스 작동 방식

모든 Application Infrastructure Bean이 AutoConfiguration을 통해 Spring Boot에 등록되어 있는지 여부는 중요하지 않습니다.

@Configuration이 있는 클래스의 경우 proxyBeanMethods는 기본적으로 true로 설정됩니다.

이 경우 클래스는 @Bean으로 메서드의 동작을 변경하기 위해 CGLib를 프록시 클래스로 사용하는 @Configuration으로 확장됩니다.

요약하면 프록시 클래스는 Spring 컨테이너가 처음 시작될 때 생성되어 설정 어노테이션이 있는 빈 객체로 사용되기 때문에 팩토리 메서드를 통해 객체를 생성하는 코드는 여러 번 호출되더라도 싱글톤으로 관리할 수 있다. (프록시 패턴이 적용되지 않으면 싱글톤으로 관리할 수 없습니다.)

설명이 좀 어려운 것 같아서 조금 더 코드로 설명하겠습니다.

먼저 MyConfig라는 객체를 선언하고 Bean1과 Bean2 모두 공통 빈에 의존한다고 가정해 보겠습니다.

이 경우 팩토리 메소드가 호출될 때마다 새로운 빈이 생성되는데 이는 Spring의 동작과 상반된다.

@Configuration
static class MyConfig {

    @Bean
    Common common() {
        return new Common();
    }

    @Bean
    Bean1 bean1() {
        return new Bean1(common());
    }

    @Bean
    Bean2 bean2() {
        return new Bean2(common());
    }
}

그러나 @Configuration 주석으로 클래스에 주석을 달고 Spring 컨테이너가 관리하도록 하면 proxyBeanMethods는 기본적으로 true 값을 가지므로 프록시 클래스가 내부적으로 생성되어 싱글톤으로 관리됩니다.

해당 테스트 코드는 아래에서 찾을 수 있습니다.

* 참고: proxyBeanMethods가 항상 참일 필요는 없습니다.

  • 프록시 생성은 비용이 많이 드는 프로세스입니다.
  • @Bean 메서드를 직접 호출하여 빈 종속성을 주입하지 않는 한 프록시를 만들 필요가 없습니다.

참조

Infron Toby의 Spring Boot – 이해 및 원리