본문 바로가기
Spring

[Spring] Auto Configuration

by 감자b 2024. 12. 28.

스프링 부트를 사용하면 DataSource, DriverManager 등 빈 등록을 하지 않아도 사용이 가능하다.

이는 스프링 부트가 자동 구성 (Auto Configuration)이라는 기능을 지원하기 때문인데, 이는 자주 사용하는 빈들을 자동으로 등록해준다.

따라서 반복적이고 복잡한 빈 등록, 설정을 최소화시켜 준다.

그렇다면 Auto Configuration이 어떻게 동작하는지 알아보도록 하겠다.


Auto Configuration 동작 방식

@SpringBootApplication
public class BootApplication {
	public static void main(String[] args) {
		SpringApplication.run(BootApplication.class, args);
	}
}

먼저 스프링 부트 메인 클래스에서 @SpringBootApplication 애노테이션 내부를 살펴보면 @EnableAutoConfiguration 이라는 애노테이션이 존재한다.

해당 애노테이션은 자동 구성을 활성화하기 위한 애노테이션이며, 내부를 살펴보면 다음과 같다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

여기서 @Import는 주로 스프링 설정 정보(@Configuration)를 포함할 때 사용하는데, AutoConfigurationImportSelector의 경우 @Configuration이 아니다.

@Import에서 설정 정보를 추가하는 방법을 2가지이다.

  • @Import(클래스) : 정적
  • @Import(ImportSelector) 설정 대상을 동적으로 지정
    • 예를 들어 다음과 같은 설정 정보가 있다고 하자.
@Configuration
 public class HelloConfig {
     @Bean
     public HelloBean helloBean() {
         return new HelloBean();
     }
}
public class HelloImportSelector implements ImportSelector {
     @Override
      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
         return new String[]{"hello.selector.HelloConfig"};
    }
}

그리고 ImportSelector 인터페이스를 구현한 HelloImportSelector는 hello.selector.HelloConfig 설정 정보를 반환하도록 한다.

마지막으로 스프링 컨테이너를 생성하고 @Import(HelloImportSelector.class)를 초기 설정 정보로 사용해서 Configuration을 만들면 스프링은 HelloImportSelector.selectImports()를 실행하고 hello.selector.HelloConfig를 반환받아 이를 설정 정보로 사용한다.

@Test
void selectorConfig() {
	AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(SelectorConfig.class);
	HelloBean bean = appContext.getBean(HelloBean.class);
	assertThat(bean).isNotNull();
}

@Configuration
@Import(HelloImportSelector.class)
public static class SelectorConfig {
}

 

스프링 부트는 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 경로의 파일에 수많은 문자열들이 존재하고 이를 읽어서 설정 정보로 사용한다.

spring-boot-autoconfigure 의존성에서 해당 파일을 확인할 수 있다.

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
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration
...

 

따라서 스프링 부트는 위에서 수많은 XxxAutoConfiguration을 자동으로 등록하는데 그 중 DataSource를 설정하는 자동 구성을 확인하면 다음과 같다.

@AutoConfiguration(
    before = {SqlInitializationAutoConfiguration.class}
)
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
@ConditionalOnMissingBean(
    type = {"io.r2dbc.spi.ConnectionFactory"}
)
@EnableConfigurationProperties({DataSourceProperties.class})
@Import({DataSourcePoolMetadataProvidersConfiguration.class})
public class DataSourceAutoConfiguration {
    public DataSourceAutoConfiguration() {
    }
    ...
}

 

여기서 @AutoConfiguration은 자동 구성을 할 때 필요한 애노테이션이다.

해당 애노테이션 내부에는 @Configuration이 존재하여 빈을 등록하는 자바 설정 파일로도 사용 가능하다.

스프링 부트를 실행하면 @EnableAutoConfiguration 애노테이션에 의해 @AutoConfiguration이 붙은 모든 클래스를 스캔하고 필터링하여 자동 구성을 활성화한다.

여기서 필터링은 @ConditionalXxx 애노테이션에 의해 이루어지는데 DataSource의 경우 DataSource.class, EmbeddedDatabaseType.class가 있고 R2DBC 타입의 빈이 존재하지 않는 경우 활성화된다는 의미이다.

@Import는 스프링에서 자바 설정을 추가할 때 사용하는 애노테이션이다.

즉 개발자가 직접 빈을 등록한다면 자동 구성이 되지않도록 하고, 자동 구성을 하는데 필요한 의존성이 classPath에 등록되면 활성화가 되도록 하는 것이다.

 

스프링 부트가 제공하는 @Conditional 애노테이션 종류는 다음과 같다.

→ Condition 인터페이스의 구현체들

  • @ConditionalOnClass , @ConditionalOnMissingClass
    • 클래스가 있을 때 동작, 없을 때 동작
  • @ConditionalOnBean, @ConditionalOnMissingBean
    • 빈이 등록되어 있을 때 동작, 없을 때 동작
  • @ConditionalOnProperty
    • 환경 정보가 있을 때 동작
  • @ConditionalOnResource
    • 리소스가 있을 때 동작
  • @ConditionalOnWebApplication, @ConditionalOnNotWebApplication
    • 웹 애플리케이션 일 때 동작, 아닐 때 동작
  • @ConditionalOnExpression
    • SpEL 표현식을 만족하는 경우 동작

 

정리하면 다음과 같다.

  1. 스프링 부트 메인 클래스 실행. (@SpringBootApplication)
  2. @EnableAutoConfiguration → @Import(AutoConfigurationImportSelector.class)
  3. resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일에서 설정 정보 선택
  4. 해당 파일의 설정 정보 중에서 @AutoConfiguration가 붙은 클래스들을 조회
  5. 해당 클래스들 중에서 Conditional 조건에 부합하는 클래스를 스프링 컨테이너에 빈으로 등록

@AutoConfiguration 도 설정 파일로 내부에 @Configuration 이 있지만 일반 스프링 설정과 라이프사이클이 다르므로 컴포넌트 스캔의 대상이 되면 안되고 파일에 지정해서 사용해야 한다.

(resources/META-INF/spring/ org.springframework.boot.autoconfigure.AutoConfiguration.imports)

스프링 부트는 실행 시 AutoConfigurationExcludeFilter 필터를 통해 @AutoConfiguration을 컴포넌트 스캔 대상에서 제외함.

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
		...
}

참고

 

스프링 부트 - 핵심 원리와 활용 강의 | 김영한 - 인프런

김영한 | 실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., 백엔드 개발자를 위한 스프링 부트 끝판왕! 실무에 필요한 내용을 모두 담았습니다.  [임베딩 영상] 김영한의 스

www.inflearn.com