Spring/Spring Boot

Spring Boot AutoConfiguration 자동 설정, 원리

TheWing 2021. 1. 3. 03:56

Spring Boot AutoConfiguration 자동 설정, 원리

  • Spring Boot는 Spring과 마찬가지로 component-scan을 통해서 component들을 찾아서 bean을 생성한다.
  • 이 과정으로 설정한 bean들(@Repository, @Service, @Controller, @RestController, @Configuration에 등록한 @Bean과 같은 설정들)이 생성된다
  • Spring Boot에서 미리 작성된 autoconfiguration에 의해 추가적인 bean들도 같이 생성된다.

@SpringBootApplication

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
    }
}

아래와 같다

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        application.run(args);
    }
}
  • @SpringBootApplication에는 @ComponentScan@EnableAutoConfiguration을 포함하고 있다
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
        //요약
}

@EnableAutoConfiguration

  • Spring Boot에서 Spring에서 많이 쓰는 스프링 빈들을 자동적으로 컨테이너에 등록하는 역할
  • autoconfiguration기능을 사용하겠다는 설정
  • component scan을 통해서 모든 component 들의 정보와 Spring Boot가 spring.factories 파일에 사전에 정의한 AutoConfiguration 내용에 의해 bean 생성이 진행된다.
  • Spring Boot의 meta 파일(spring-boot-autoconfigure/META-INF/spring.factories)을 읽어서 미리 정의 되어있는 자바 설정 파일(@Configuration)들을 빈으로 등록하는 역할을 수행 한다

spring-boot-autoconfigure/META-INF/spring.factories

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
....

이렇게 spring.factories안에는 여러가지 autoConfiguration들이 정의되어 있다.

이 키값에 설정되어 있는 것들은 모두 autoConfiguration의 대상이되며 적용된다. (단, 조건에 따라서 등록이 안될 수도 있다.)

@ComponentScan

  • 해당 패키지에서 @Componont 어노테이션을 가진 Bean들을 스캔해서 등록한다. (@Configuration, @Repository, @Service, @Controller, @RestController)
  • @ComponentScan("me.thewing.sample")
    • 해당 패키지 하위 모든 패키지를 component scan 범위로 잡겠다는 설정이다 . package 위치를 입력하지 않으면 Application이 있는 패키지가 기본값으로 사용된다.

Auto Configuration Filters , Conditions

  • Spring Boot가 미리 정해둔 AutoConfiguration 정보는 위에서 말했듯이 spring.factories에서 확인이 가능하다
  • AutoConfiguration 들은 필요한 상황에만 자신이 실행 될 수 있도록 @Conditional, @Condition 과 같은 annotation들로 설정되어 있다. 그 어노테이션을 기반으로 필터링이 먼저 이뤄지고 필터링이 되지 않은 AutoConfiguration 을 가지고 작업이 진행된다
  • @Condition, @Conditional Spring 4.0부터 추가된 어노테이션이고 Spring Boot auto configuration 과정에서 사용되는 다른 어노테이션들도 autoconfigure-condition에서 확인이 가능하다
  • @Profile, @Lazy와 같은 Spring 에서 제공하는 다른 어노테이션들도 Spring Boot auto configuration 에 활용이된다.

Condition interface

  • 구성 요소를 등록하기 위해 일치해야하는 단일 조건이다
  • 빈 정의가 등록되기 직전에 조건을 확인하고 해당 시점에 결정될 수 있는 기준에 따라 등록을 거부할 수 있다.
  • 사용 시점
    • property나 environment등 조건을 파악할때 사용된다

@Conditional

  • 어노테이션으로 조건부 Bean을 스프링 컨테이너에 등록하는 역할을 한다. Condition Interface를 사용하여 특정 조건부로 등록되도록 할 수 있다.
  • Conditional을 확장한 어노테이션은 @ConditionalOnMissingBean, @ConditionalOnBean, @ConditionalOnClass 등이 있다
  • 조건을 지정하기 위한 클래스이다

Auto Configuration Import Filters

  • Spring Boot는 spring.factories 정보를 가지고 auto configration을 진행한다
  • AutoConfigurationImportFilter 관련 설정이 있으며 아래와 같은 3개의 필터가 적용된 것을 확인할 수 있다

spring.factories

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
//생략

해당 필터들은 AutoConfiguration 이 가진 @Conditional을 가지고 조건 만족 여부를 체크한다. 그리고 조건이 맞지 않을 경우에 해당 AutoConfiguration 이 동작하지 않도록 제외 시키는 역할을 수행한다

Condition interface의 구현체

SpringBootCondition implement Condition

  • Spring Boot와 함께 사용되는 모든 조건 구현의 기초이다. 사용자가 로드된 클래스를 진단하는데 도움이 되는 적절한 로깅을 제공한다

interface ConfigurationCondition implements Condition

  • @Configuration과 함께 사용할 때보다 세밀한 제어를 제공하는 조건입니다. 특정 조건이 구성 단계에 따라 일치 할 때 적응할 수 있습니다. 예를 들어 빈이 이미 등록되었는지 확인하는 조건은 REGISTER_BEAN ConfigurationCondition.ConfigurationPhase 동안에 만 평가되도록 선택할 수 있습니다.

  • org.springframework.boot.autoconfigure.condition.OnBeanCondition

    • 특정 bean들의 존재유무를 확인하는 필터이다
    • 적용 대상
      • @ConditionalOnBean, @ConditionalOnMissingBean, @ConditionalOnSingleCandidate
  • org.springframework.boot.autoconfigure.condition.OnClassCondition

    • 특정 class들의 존재 유무를 확인하는 필터이다
    • 적용 대상
      • @ConditionalOnClass, @ConditionalOnMissingClass
  • org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

    • WebApplicationContext의 존재 유무를 확인하는 필터이다
    • 적용 대상
      • @ConditionalOnWebApplication, @ConditionalOnNotWebApplication

@ConditionalOnMissingBean

  • @ConditionalOnMissingBean특정 bean이 사전에 생성되지 않은 때 조건이 성립한다. @Bean과 함께 사용된다면 이미 생성된 bean이 없을 때 해당 bean을 생성한다
  • 특정 bean을 생성하도록 설정해놨다면, 일반적으로 AutoConfiguration의 bean생성 순서가 마지막에 온다
  • 직접 생성된 bean이 먼저 생성되고 해당 AutoConfiguration은 필터링되어 중복생성되는 상황을 막는다. 해당 bean을 설정하지 않았다면 AutoConfiguration에서는 해당 bean을 자동 생성하게 된다
  • ThreadPoolTaskExecutor bean을 생성하는 TaskExecutionAutoConfiguration.java를 예로 들어보자

TaskExecutionAutoConfiguration.java

// 생략
@Configuration(proxyBeanMethods = false)
public class TaskExecutionAutoConfiguration {

    /**
     * Bean name of the application {@link TaskExecutor}.
     */
    public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";

        // 생략

    @Lazy
    @Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
        AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
    @ConditionalOnMissingBean(Executor.class)
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }

}

@Lazy가 걸려 있기 때문에 Spring Boot 실행시 생성되지 않고 ThreadPoolTaskExecutor가 필요한 상황에서 bean이 생성이 요청된다. Executor.class와 같은 class type인 bean이 이미 생성되지 않은 경우에 @ConditionalOnMissiongBean 조건이 만족되고 bean 생성이 진행된다. 즉 , 아래와 같은 Executor bean을 생성하는 설정을 했다면 설정한 bean이 생성되고 TaskExecutionAutoConfiguration에 의해서는 bean이생성 되지 않는다. 반대로 Executor bean등록을 설정하지 않았더라도 필요한 상황이 되면 해당 bean이 생성되게 된다

@Lazy, @Primary

  • @Lazy
    • 빈들이 등록되고 나중에 등록된다
    • getBean 호출 시 빈으로 등록이 된다
  • @Primary
    • 기본으로 등록되는 빈이다
    • 어느 것이 등록되어도 @Primary 가 있는 빈이 항상 등록이 된다
    • @Primary 를 두개 사용하면 에러난다

AsyncConfigSample.java

//생략
@Configuration
public class AsyncConfigSample {

    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(2);
        taskExecutor.initialize();
        return taskExecutor;
    }
}

@ConditionalOnBean

  • @ConditionalOnBean특정 bean이 이미 생성 되어 있을 때만 조건이 성립한다. 작업을 위해 필수적으로 필요한 bean이 미리 생성 되었는지 확인하는 용도로 사용할 수 있다.

@ConditionalOnClass

  • @ConditionalOnClassclasspath에 특정 class가 존재할 때 조건이 성립한다. 작업을 위해 필수적으로 필요한 의존성이 등록되어 있는지 확인하는 용도로 사용할 수 있다.

메소드에 @ConditionalOnClass를 사용하면 Exception이 발생한다

@Configuration 
public class TestConfiguration { 
    @Bean @ConditionalOnClass(TestBean.class) 
    public TestBean testBean() { 
        return new TestBean(); 
    } 
}
  • ClassNotFoundException이 발생한다
@Configuration 
public class TestConfiguration { 
    @Bean 
    @ConditionalOnClass(Test.class) 
    public TestBean testBean() { 
        return new TestBean(); 
    } 
}
  • ArrayStoreException(TypenotPresentExceptionProxy) 이 발생한다

왜 Exception이 발생하는지?

  • ConfigurationClassPostProcessor Class는 @Configuration 어노테이션이 적용된 빈 객체에서 @Bean 어노테이션이 적용된 메서드로 부터 빈 객체를 가져와 스프링 컨테이너에 등록하는 역할을한다

ConfigurationClassPostProcessor

  1. ConfigurationClassParser 를 통해 먼저 Class의 생성 여부를 @Conditional 어노테이션들을 포함한 특수한 과정을 통해 정의한다
  2. 위 과정을 통과한 Configuration Class는 ConfigurationClassEnhancerConfiguration 를 통해 Enhance라는 ByteCode를 읽어 들이는 과정을 거쳐 Class를 로드하게 된다

Appendices

Disabling Specific Auto-configuration Classes

  • 만약 특정 AutoConfiguration을 사용하지 않으려고 하면 아래와 같이 exclude 설정을 하면된다
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;

@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
        // 생략
}

참고

https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-auto-configuration

'Spring > Spring Boot' 카테고리의 다른 글

@Log4j2 vs @Slf4j with log4j2  (0) 2021.01.22