Spring Boot 자동 환경 설정하기
Spring Boot의 자동 환경 설정 방법에 대해 정리한 내용입니다.
1. 자동 환경 설정 Annotation
Spring Boot에서는 Web, H2, JDBC를 비롯한 약 100여 개의 자동 환경 설정을 제공합니다. JAR와 같은 라이브러리를 새로 추가한 경우에도 Spring Boot의 자동 환경 설정의 의존성에 따라 자동으로 설정이 적용됩니다. 기존의 Spring Framework에서는 의존성을 Bean으로 설정했었는데 Spring Boot에서는 의존성을 starter라는 묶음으로 제공하며 수동 설정 방식을 지양하고 있습니다.
자동 환경 설정을 사용하기 위해서는 @EnableAutoCoonfiguration 또는 @SpringBootApplication 어노테이션 중 하나를 사용하면 됩니다. 단, @EnableAutoConfiguration 어노테이션을 사용하는 경우엔 반드시 @Configuration 어노테이션을 함께 사용해줘야 합니다.
1.1 @SpringBootApplication
@SpringBootApplication 어노테이션은 @SpringBootConfiguration과 @EnableAutoConfiguration, @ComponentScan 어노테이션의 조합입니다.
다음은 @SpringBootApplication의 내부를 간소화한 코드입니다.
@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 {
...
}
@SpringBootApplication 내부에서 사용된 주요 어노테이션에 대한 설명은 다음과 같습니다.
- @SpringBootConfiguration
Spring Boot의 설정을 나타내는 어노테이션.
Spring의 @Configuration을 대체하여 Spring Boot 전용으로 사용되는 필수 어노테이션. - @EnableAutoConfiguration
자동 설정을 사용하기 위한 핵심 어노테이션.
classpath에 지정된 내용을 기반으로 설정 자동화를 수행.
특별한 설정값을 추가하지 않은 경우 기본값으로 동작. - @ComponentScan
특정 패키지 경로를 기반으로 @Configuration에서 사용할 @Component 설정 클래스를 찾아옴.
@ComponentScan의 basePackages에 별도로 경로를 설정하지 않으면, @ComponentScan이 위치한 패키지가 basePackages의 루트 경로로 설정됨.
1.2 @EnableAutoConfiguration
@EnableAutoConfiguration은 자동 설정의 핵심 어노테이션입니다. 아래는 @EnableAutoConfiguration의 내부를 간소화한 코드입니다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
@EnableAutoConfiguration 내부에서 자동 설정을 지원해주는 어노테이션은 @Import(AutoConfigurationImportSelector.class) 입니다. Bean의 등록과 자동 설정에 필요한 내용이 작성되어 있습니다.
AutoConfigurationImportSelector 클래스는 DeferredImportSelector 인터페이스를 구현한 클래스이고, 오버라이드 받은 selectImports() 메서드가 자동 설정할 Bean을 결정합니다. getCandidateConfigurations() 메서드가 미리 정의된 약 100여 개의 Bean을 META-INF/spring.factories 에서 불러옵니다.
여러 개의 spring-boot-starter를 사용할 경우엔 내부에 중복된 Bean 설정이 많아 지는데, 이러한 경우 getExclusions(), removeDuplicates() 메서드가 제외시켜 줍니다. 이후에 프로젝트에 사용하게 될 남은 Bean 들이 자동 설정 대상으로 선택됩니다.
public class AutoConfigurationImportSelector implements DeferredImportSelector,
BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata
) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
...
}
Bean 등록과 자동 설정에 사용되는 파일은 다음과 같습니다. 아래 파일들 모두 spring-boot-autoconfiguration에 미리 정의되어 있으며 지정된 프로퍼티값을 사용하여 설정 클래스 내부의 값들을 변경할 수 있습니다.
- META-INF/spring.factories
자동 설정 대상 클래스 목록.
여기에 선언된 클래스들이 @EnableAutoConfiguration 사용시 자동 설정 대상으로 지정됨. - META-INF/spring-configuration-metadata.json
자동 설정에 사용할 프로퍼티 정의 파일.
미리 구현되어 있는 자동 설정에 프로퍼티만 주입시켜 주므로 별도의 환경 설정은 필요하지 않음 - org/springframework/boot/autoconfigure
미리 구현한 자동 설정 목록.
'{특정 설정의 이름}AutoCnfiguration' 형식으로 지정되어 있으며 모두 자바 설정 방식을 따름.
2. 자동 설정 Annotation
Spring Boot는 자동 설정이 적용되는 조건, 시점 등에 따라 다양한 어노테이션을 지원합니다. 이를 잘 알아두면 설정 관리 능력을 향상시키고 최적화된 자동 설정을 만들 수도 있습니다.
다음은 자동 설정을 위한 조건 어노테이션과 각각의 적용 조건입니다. 적용 조건을 만족했을 때 자동 설정이 적용됩니다.
조건 어노테이션 | 적용 조건 |
@ConditionalOnBean | 해당 Bean 이나 이름이 미리 Bean 팩토리에 포함되어 있는 경우 |
@ConditionalOnClass | 해당 클래스가 classpath에 있는 경우 |
@ConditionalOnCloudPlatform | 해당 클라우드 플랫폼이 활용 상태인 경우 |
@ConditionalOnExpression | SpEL에 의존하는 조건인 경우 |
@ConditionalOnJava | JVM 버전이 일치하는 경우 |
@ConditionalOnJndi | JNDI가 사용 가능하고 특정 위치에 있는 경우 |
@ConditionalOnMissingBean | 해당 Bean 이나 이름이 미리 Bean 팩토리에 포함되어 있지 않은 경우 |
@ConditionalOnMissingClass | 해당 클래스가 classpath에 없는 경우 |
@ConditionalOnNotWebApplication | 웹 애플리케이션이 아닌 경우 |
@ConditionalOnProperty | 특정 프로퍼티가 지정한 값을 갖는 경우 |
@ConditionalOnResource | 특정 Resource가 classpath에 있는 경우 |
@ConditionalOnSingleCandidate | 지정한 Bean이 이미 Bean 팩토리에 포함되어 있고 단일 후보자로 지정 가능한 경우 |
@ConditionalOnWebApplication | 웹 애플리케이션인 경우 |
다음은 자동 설정을 위한 순서 어노테이션입니다.
순서 어노테이션 | 설명 |
@AutoConfigureAfter | 지정한 특정 자동 설정 클래스들이 적용된 후에 해당 자동 설정을 적용 |
@AutoConfigureBefore | 지정한 특정 자동 설정 클래스들이 적용되기 전에 해당 자동 설정을 적용 |
@AutoConfigureOrder | 자동 설정 순서 지정을 위한 Spring Framework @Order의 변형 어노테이션 (기존 설정 클래스에는 영향을 주지 않고 자동 설정 클래스들 간의 순서만 지정) |
다음으로 H2ConsoleAutoConfiguration.java 클래스를 통해 H2 자동 설정을 위한 어노테이션이 어떻게 사용되는지 알아보겠습니다.
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(WebServlet.class)
@ConditionalOnProperty(
prefix = "spring.h2.console",
name = "enabled",
havingValue = "true",
matchIfMissing = false
)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(H2ConsoleProperties.class)
public class H2ConsoleAutoConfiguration {
private static final Log logger = LogFactory.getLog(H2ConsoleAutoConfiguration.class);
@Bean
public ServletRegistrationBean<WebServlet> h2Console(
H2ConsoleProperties properties,
ObjectProvider<DataSource> dataSource
) {
String path = properties.getPath();
String urlMapping = path + (path.endsWith("/") ? "*" : "/*");
ServletRegistrationBean<WebServlet> registration =
new ServletRegistrationBean<>(new WebServlet(), urlMapping);
H2ConsoleProperties.Settings settings = properties.getSettings();
if (settings.isTrace()) {
registration.addInitParameter("trace", "");
}
if (settings.isWebAllowOthers()) {
registration.addInitParameter("webAllowOthers", "");
}
dataSource.ifAvailable((available) -> {
try (Connection connection = available.getConnection()) {
logger.info(
"H2 console available at '" + path + "'. Database available at '"
+ connection.getMetaData().getURL() + "'"
);
}
catch (SQLException ex) {
// Continue
}
});
return registration;
}
...
}
아래 3개의 어노테이션이 조건에 부합할 때 H2ConsoleAutoConfiguration 클래스가 적용됩니다. 주요 자동 설정 어노테이션에 대한 설명은 다음과 같습니다.
- @ConditionalOnWebApplication(type = Type.SERVLET)
웹 애플리케이션일 때 적용 - @ConditionalOnClass(WebServlet.class)
WebServlet.class가 클래스 경로에 있을 때 적용 - @ConditionalOnProperty(prefix = "spring.h2.console", name = "enabled", havingValue = "true", matchIfMissing = false)
spring.h2.console.enabled 값이 true 일 때 적용
위의 조건이 부합하여 자동 설정이 적용될 때 H2ConsoleProperties.class 타입으로 H2 관련 프로퍼티값을 매핑하여 사용하게 됩니다. 기존의 스프링 프레임워크에서는 일일이 설정해야 했지만 스프링 부트에서는 미리 설정한 방식대로 애플리케이션에 적용하도록 정의되어 있습니다.
이상으로 Spring Boot의 자동 환경 설정에 대해 알아봤습니다.
※ References
- 김영재 지음, 『처음 배우는 스프링 부트 2』, 한빛미디어 (2019), p62 ~ p69. CHAPTER 2 스프링 부트 환경 설정