-
Spring Boot 핵심 기능 3 - 자동 구성(Auto Configuration)BackEnd/Spring Boot 2023. 4. 20. 07:00반응형
스프링 부트의 자동 구성
스프링 부트는 자동 구성(Auto Configuration) 기능을 제공합니다. 일반적으로 자주 사용하는 수 많은 빈들을 자동으로 등록해주는 기능입니다. JdbcTemplate, DataSource, TransactionManager 등 스프링 부트가 자동 구성을 제공해서 자동으로 스프링 빈으로 등록됩니다.
스프링 부트 자동 구성이 동작하는 원리는 다음 순서로 확인할 수 있습니다.
- @SpringBootApplication
- @EnableAutoConfiguration
- @Import(AutoConfigurationImportSelector.class)
- resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 파일의 설정 정보가 스프링 컨테이너에 등록되고 사용
스프링 부트는 spring-boot-autoconfiguration 프로젝트 안에서 수 많은 자동 구성을 제공합니다. JdbcTemplate을 설정하고 빈으로 등록해주는 자동 구성은 다음과 같습니다.
package org.springframework.boot.autoconfigure.jdbc; import javax.sql.DataSource; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer; import org.springframework.context.annotation.Import; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @AutoConfiguration(after = DataSourceAutoConfiguration.class) @ConditionalOnClass({ DataSource.class, JdbcTemplate.class }) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties(JdbcProperties.class) @Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class }) public class JdbcTemplateAutoConfiguration { }
- @AutoConfiguration: 자동 구성을 사용하려면 이 애노테이션을 등록해야 합니다.
- after = DataSourceAutoConfiguration.class: 자동 구성이 실행되는 순서를 지정할 수 있습니다. JdbcTemplate은 DataSource가 필요하기 때문에 DataSource를 자동으로 등록해주는 DataSourceAutoConfigration 다음에 실행하도록 설정되어 있습니다.
- @ConditionalOnClass({ DataSource.class, JdbcTemplate.class }): IF문과 유사한 기능을 제공합니다. DataSource, JdbcTemplate 클래스가 있는 경우에만 설정이 동작합니다. 만약 없으면 여기 있는 설정들이 모두 무효화 되고, 빈도 등록되지 않습니다.
- @Import: 스프링에서 자바 설정을 추가할 때 사용합니다.
@Import의 대상이 되는 JdbcTemplateConfiguration은 다음과 같습니다.
package org.springframework.boot.autoconfigure.jdbc; import javax.sql.DataSource; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcTemplate; @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(JdbcOperations.class) class JdbcTemplateConfiguration { @Bean @Primary JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); JdbcProperties.Template template = properties.getTemplate(); jdbcTemplate.setFetchSize(template.getFetchSize()); jdbcTemplate.setMaxRows(template.getMaxRows()); if (template.getQueryTimeout() != null) { jdbcTemplate.setQueryTimeout((int) template.getQueryTimeout().getSeconds()); } return jdbcTemplate; } }
- @Configuration: 자바 설정 파일로 사용됩니다.
- @ConditionalOnMissingBean(JdbcOperations.class): JdbcOperation 빈이 없을 때 동작합니다. JdbcOperations는 JdbcTemplate의 부모 인터페이스입니다. 즉, JdbcTemplate가 빈으로 등록되어 있지 않은 경우에만 동작합니다. 만약 이런 기능이 없으면 내가 등록한 JdbcTemplate과 자동 구성이 등록하는 JdbcTemplate가 중복 등록되는 문제가 발생할 수 있습니다. 개발자가 직접 빈을 등록하면 개발자가 등록한 빈을 사용하고, 자동 구성은 동작하지 않습니다.
자동 등록 설정
다음과 같은 자동 구성 기능들이 다음 빈들을 등록해줍니다.
- JdbcTemplateAutoConfiguration : JdbcTemplate
- DataSourceAutoConfiguration : DataSource
- DataSourceTransactionManagerAutoConfiguration : TransactionManager
스프링 부트가 제공하는 자동 구성(AutoConfiguration)
스프링 부트는 수 많은 자동 구성을 제공하고 spring-boot-autoconfiguration에 자동 구성을 모아둡니다. 스프링 부트 프로젝트를 사용하면 spring-boot-autoconfigure 라이브러리는 기본적으로 사용됩니다.
@Conditional
같은 소스 코드인데 특정 상황일 때(예: 개발 환경에서는 Swagger를 사용하고 운영 환경에서는 Swagger를 사용하지 않을 때)만 특정 빈들을 등록해서 사용하도록 도와주는 기능입니다. 스프링 부트 자동 구성에서 자주 사용합니다. 이 기능을 사용하려면 Condition 인터페이스를 구현해야 합니다.
@FunctionalInterface public interface Condition { /** * Determine if the condition matches. * @param context the condition context * @param metadata the metadata of the {@link org.springframework.core.type.AnnotationMetadata class} * or {@link org.springframework.core.type.MethodMetadata method} being checked * @return {@code true} if the condition matches and the component can be registered, * or {@code false} to veto the annotated component's registration */ boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
- matches(): true를 반환하면 조건에 만족해서 동작하고, false를 반환하면 동작하지 않습니다.
- ConditionContext: 스프링 컨테이너, 환경 정보 등을 담고 있습니다.
- AnnotatedTypeMetadata: 애노테이션 메타 정보를 담고 있습니다.
다음은 환경 정보가 memory=on이라고 되어 있을 때만 동작하는 샘플 코드입니다.
package memory; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; @Slf4j public class MemoryCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //-Dmemory=on String memory = context.getEnvironment().getProperty("memory"); log.info("memory={}", memory); return "on".equals(memory); } }
package hello.config; import memory.MemoryCondition; import memory.MemoryController; import memory.MemoryFinder; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration @Conditional(MemoryCondition.class) public class MemoryConfig { @Bean public MemoryController memoryController() { return new MemoryController(memoryFinder()); } @Bean public MemoryFinder memoryFinder() { return new MemoryFinder(); } }
스프링은 대부분의 필요한 Condition 인터페이스 구현체를 만들어 제공합니다. 위의 코드는 아래와 같이 사용할 수 있습니다. 별도의 MemoryCondition.class 를 구현할 필요 없이 @ConditionalOnProperty을 사용하면 됩니다.
@Configuration //@Conditional(MemoryCondition.class) @ConditionalOnProperty(name = "memory", havingValue = "on") //추가 public class MemoryConfig { ... 생략 ... }
- @ConditionalOnClass , @ConditionalOnMissingClass: 클래스가 있는 경우 동작한다. 나머지는 그 반대
- @ConditionalOnBean , @ConditionalOnMissingBean: 빈이 등록되어 있는 경우 동작한다. 나머지는 그 반대
- @ConditionalOnProperty: 환경 정보가 있는 경우 동작한다.
- @ConditionalOnResource: 리소스가 있는 경우 동작한다.
- @ConditionalOnWebApplication , @ConditionalOnNotWebApplication: 웹 애플리케이션인 경우 동작한다.
- @ConditionalOnExpression: SpEL 표현식에 만족하는 경우 동작한다.
[참고정보]
반응형'BackEnd > Spring Boot' 카테고리의 다른 글
Spring Boot 핵심 기능 4 - 외부설정(2) (0) 2023.05.04 Spring Boot 핵심 기능 4 - 외부설정(1) (0) 2023.05.03 Spring Boot 핵심 기능 2 - 라이브러리 관리 (0) 2023.04.19 Spring Boot 핵심 기능 1 - 내장 톰캣 (0) 2023.03.25 REST API (0) 2022.05.25