[Spring Boot] 자동 구성
DB에 회원을 저장하고 관리하려면 JdbcTemplate, DataSource, TransactionManager 등 데이터베이스 관련 여러 빈들이 사용된다.
스프링 부트는 자동 구성 기능을 사용해 자주 사용하는 빈들을 자동으로 등록해준다.
spring-boot-autoconfigure 프로젝트 내부에서 자동 구성 관련 정보를 확인할 수 있다. (spring-boot-starter 라이브러리를 추가할 때 등록된다)
@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 {
}
스프링 부트 프로젝트를 github에서 받아서 확인해보면 다양한 AutoConfiguration 클래스를 확인할 수 있다.
위의 예시는 JdbcTemplate을 자동 설정하는 클래스이다.
@AutoConfiguration : 자동 구성을 사용하려면 해당 애너테이션을 사용해야 한다. (애너테이션 내부에 @Configuration 이 있어 해당 클래스를 빈 등록 설정 파일로 사용할 수 있다)
@ConditionalOnClass : 예시에서는 DataSource.class와 JdbcTemplate.class 가 인자로 들어온다. 인자로 들어온 클래스들이 존재하는 경우에만 자동 설정을 진행한다.
@Import : 스프링에서 자바 설정을 추가할 때 사용한다.
애너테이션들을 하나씩 살펴보자.
@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;
}
}
@Import 애너테이션으로 추가한 JdbcTemplateConfiguration 이다.
@ConditionalOnMissingBean : JdbcOperation.class 라는 빈이 존재하지 않는 경우 자동 설정을 진행한다. (JdbcOperation은 JdbcTemplate이 구현하는 인터페이스이다)
@Conditional 애너테이션은 if-else 문법과 비슷하게 생각하면 이해하기 편하다.
즉, 개발자가 직접 등록한 빈이 있다면 Conditional 애너테이션이 인식해 자동 설정을 진행하지 않아 빈이 중복 등록되는 문제를 해결한다.
JdbcTemplate, DataSource, TransactionManager 뿐만 아니라 스프링 부트는 수 많은 자동 구성을 제공하니 참고하자.
기존에 라이브러리를 통해 받아온 클래스들을 사용하려면 빈으로 등록하는 절차를 거쳐야 했다.
@Configuration
public class TemConfig {
@Bean
public TempController TempController {
return new TempController(tempDependency);
}
@Bean
public TempDependency tempDependency {
return new tempDependency();
}
}
부트의 자동 설정 기능을 사용하지 않는다면 위와 같이 하나하나 빈으로 등록하는 작업이 필요하다.
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
부트의 자동 구성을 이해하기 위해 @Conditional 애너테이션을 살펴보자.
Condition 인터페이스는 @Conditional 애너테이션이 적용될 조건을 지정할 때 사용된다.
public class TempCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
@Configuration
@Conditional(TempCondition.class)
public class TemConfig {
@Bean
public TempController TempController {
return new TempController(tempDependency);
}
@Bean
public TempDependency tempDependency {
return new tempDependency();
}
}
Condition 인터페이스를 구현해서 Condition 클래스를 만들고 @Conditoinal 애너테이션에 만든 Condition 클래스를 지정해 주면 matches 메서드의 결과값이 true 인 경우에만 해당 설정을 사용하도록 지정한다.
matches 메서드를 적절히 오버라이드 할 때 외부 환경 설정 정보를 참고해서 true나 false를 리턴하도록 설정할 수 있으니 응용해서 활용해보자.
스프링 부트는 @Conditional 애너테이션을 응용해서 @ConditionalOnProperty, @ConditionalOnClass 등 수많은 @ConditionalOn ... 애너테이션을 제공하니 정말 특수한 기능이 아니면 이미 있는 기능을 찾아서 사용하자. (내부적으로는 위와 비슷한 방식으로 동작한다)
일반적으로 라이브러리를 사용할 때는 프로젝트 내부의 libs 디렉토리에 라이브러리를 넣고 gradle의 dependency 부분에 해당 라이브러리를 명시해주면 된다.
이후 @Configuration과 @Bean으로 빈으로 등록해준 후 적당히 주입받아서 사용하면 되는데....
라이브러리를 넣고 의존성도 추가해줬다고 하자. 빈으로 등록할 때는 문제가 발생한다.
처음 사용하는 입장에서는 어떤 클래스들을 빈으로 등록해야 하는지 알기 어렵다.
부트의 자동 구성은 빈 등록 작업을 자동화한다.
@AutoConfiguration
@ConditionalOnProperty(name = "n", havingValue = "asdf")
public class TempAutoConfig {
@Bean
public TempController TempController {
return new TempController(tempDependency);
}
@Bean
public TempDependency tempDependency {
return new tempDependency();
}
}
자동 구성 기능을 적용할 때는 @AutoConfiguration 애너테이션을 사용한다.
@ConditionalOnProperty 애너테이션으로 환경 변수 정보에 따라 해당 자동 설정을 진행하도록 설정했다.
자동 설정 클래스를 작성하기만 하면 끝나는게 아니다.
스프링이 해당 클래스를 알 수 있도록 명시해 줘야 한다.
src/main/resources/META-INF/spring/...imports 경로에 자동 설정 클래스를 패키지를 포함해서 입력하자.
@AutoConfiguration 을 적용하면 라이브러리를 사용할 때 해당 클래스들을 빈으로 등록할 필요 없이 바로 주입받아서 사용할 수 있다.
spring-boot-autoconfigure 라이브러리에는 스프링이 기본으로 제공하는 자동 구성 관련 정보가 있다.
개인적으로 추가한 파일을 포함해서 모든 라이브러리들을 탐색하고 해당 정보를 읽어서 AutoConfiguration으로 사용할 수 있도록 준비한다.
@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 { ... }
///
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration { ... }
스프링 부트의 메인 메서드가 포함된 클래스에는 @SpringBootApplication 애너테이션이 붙어있다.
@SpringBootApplication 애너테이션 내부에는 @EnableAutoConfiguration 애너테이션과 @Import(AutoConfigurationImportSelector.class) 애너테이션이 붙어있고, 해당 애너테이션은 AutoConfiguration을 활성화하는 역할을 수행한다.
@Import 애너테이션은 @Configuration 으로 작성된 설정 정보를 포함할 때 사용되지만... AutoConfigurationImportSelector.class 클래스는 @Configuration 애너테이션을 포함하지 않는다. (보통 설정 정보가 여러 개인 경우 사용된다)
@Import 애너테이션은 설정으로 사용할 대상을 정적으로 설정할 수도 있고, 동적으로 설정할 수도 있다.
동적으로 설정할 때 ImportSelector 인터페이스가 사용되고, 인터페이스의 구현체를 사용해 동적 설정을 구현한다.
public class TempConfig implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[0];
}
}
구현체는 위와 같은 형태로 작성된다. 사용할 설정 클래스를 배열에 담아서 넘겨준다.
동적으로 설정 클래스를 추가하기에 특정 조건에 따라 특정 설정 정보를 사용하도록 설정할 수 있다.
즉, 스프링 부트 애플리케이션을 실행하면 @SpringBootApplication 애너테이션이 실행되고
@EnableAutoConfiguration 애너테이션이 실행되고, @Import(AutoConfigurationImportSelector.class) 애너테이션이 실행돼 설정 정보를 동적으로 선택한다.
모든 라이브러리에 있는 자동 설정 관련 경로를 확인해 컨테이너에 등록 후 사용한다. (코드를 까고 디버깅 해 보자!)
라이브러리를 만들 때 @AutoConfiguration 애너테이션을 붙여서 설정 클래스를 만들고, 라이브러리의 사용자들이 빈 등록 없이 쉽게 사용할 수 있도록 하자.
@AutoConfiguration 클래스는 빈으로 등록하지 않고 파일을 지정해서 사용하도록 설정하고, 상위에 @Configuration 애너테이션이 붙어있어 설정 클래스로 활용할 수 있다.
@AutoConfiguration 은 라이브러리를 만들 때 사용된다!
'Spring > Spring Boot' 카테고리의 다른 글
[Spring Boot] 외부 설정 (0) | 2023.05.28 |
---|---|
[Spring Boot] 라이브러리의 선택과 버전 관리 (0) | 2023.05.23 |
[Spring Boot] 부트와 내장 WAS (0) | 2023.05.22 |
[Spring Boot] 스프링과 웹 서버 (0) | 2023.05.21 |
[Spring Boot] 스프링 부트의 도입 (0) | 2023.05.14 |
댓글
이 글 공유하기
다른 글
-
[Spring Boot] 외부 설정
[Spring Boot] 외부 설정
2023.05.28 -
[Spring Boot] 라이브러리의 선택과 버전 관리
[Spring Boot] 라이브러리의 선택과 버전 관리
2023.05.23 -
[Spring Boot] 부트와 내장 WAS
[Spring Boot] 부트와 내장 WAS
2023.05.22 -
[Spring Boot] 스프링과 웹 서버
[Spring Boot] 스프링과 웹 서버
2023.05.21