@Conditional
- 앞서 만든 메모리 조회 기능을 항상 사용하는 것이 아니라 특정 조건일 때만 해당 기능이 활성화 되도록 해보자.
- 예를 들어서 개발 서버에서 확인 용도로만 해당 기능을 사용하고, 운영 서버에서는 해당 기능을 사용하지 않는 것이다.
- 여기서 핵심은 소스코드를 고치지 않고 이런 것이 가능해야 한다는 점이다.
- 프로젝트를 빌드해서 나온 빌드 파일을 개발 서버에도 배포하고, 같은 파일을 운영서버에도 배포해야 한다.
- 같은 소스 코드인데 특정 상황일 때만 특정 빈들을 등록해서 사용하도록 도와주는 기능이 바로 @Conditional 이다.
- 참고로 이 기능은 스프링 부트 자동 구성에서 자주 사용한다.
지금부터 @Conditional에 대해서 자세히 알아보자. 이름 그대로 특정 조건을 만족하는가 하지 않는가를 구별하는 기능이다.
이 기능을 사용하려면 먼저 Condition 인터페이스를 구현해야 한다. 그전에 잠깐 Condition 인터페이스를 살펴보자.
Condition
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
- matches() 메서드가 true 를 반환하면 조건에 만족해서 동작하고, false 를 반환하면 동작하지 않는다.
- ConditionContext : 스프링 컨테이너, 환경 정보등을 담고 있다.
- AnnotatedTypeMetadata : 애노테이션 메타 정보를 담고 있다.
Condition 인터페이스를 구현해서 다음과 같이 자바 시스템 속성이 memory=on이라고 되어 있을 때만 메모리 기능이 동작하도록 만들어보자.
#VM Options
java -Dmemory=on -jar project.jar
코드 작성
MemoryCondition
@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);
}
}
- 환경 정보에 memory=on 이라고 되어 있는 경우에만 true 를 반환한다.
MemoryConfig-수정
@Configuration
@Conditional(MemoryCondition.class) //스프링이 뜰때, 먼저 실행되어 검증한다.
public class MemoryConfig {
@Bean
public MemoryFinder memoryFinder(){
return new MemoryFinder();
}
@Bean
public MemoryController memoryController(){
return new MemoryController(memoryFinder());
}
}
- @Conditional(MemoryCondition.class)
- 이제 MemoryConfig 의 적용 여부는 @Conditional 에 지정한 MemoryCondition 의 조건에 따라 달라진다.
- MemoryCondition 의 matches() 를 실행해보고 그 결과가 true 이면 MemoryConfig 는 정상 동작한다. 따라서 memoryController , memoryFinder 가 빈으로 등록된다.
- MemoryCondition 의 실행결과가 false 이면 MemoryConfig 는 무효화 된다. 그래서 memoryController , memoryFinder 빈은 등록되지 않는
결과

- 이와 같이 환경설정을 주어야지만 빈이 정상적으로 등록이 되어 localhost:8080/memory 로 접근이 가능하다.
- MemoryCondition 조건이 true 를 반환해서 빈이 정상 등록된다.
참고

스프링은 외부 설정을 추상화해서 Enviroment 로 통합했다. 그래서 다음과 같은 다양한 외부 환경 설정을 environment하나로 읽어 들일 수 있다. 여기에 대한 더 자세한 내용은 뒤에서 다룬다
@Conditional 다양한 기능
지금까지 Condition 인터페이스를 직접 구현해서 MemoryCondition이라는 구현체를 만들었다. 스프링은 이미 필요한 대부분의 구현체를 만들어두었다. 이번에는 스프링이 제공하는 편리한 기능을 사용해 보자..
MemoryConfig-수정
@Configuration
//@Conditional(MemoryCondition.class) //스프링이 뜰때, 먼저 실행되어 검증한다.
@ConditionalOnProperty(name = "memory", havingValue = "on")
public class MemoryConfig {
@Bean
public MemoryFinder memoryFinder(){
return new MemoryFinder();
}
@Bean
public MemoryController memoryController(){
return new MemoryController(memoryFinder());
}
}
- @Conditional(MemoryCondition.class) 를 주석처리하자
- @ConditionalOnProperty(name = "memory", havingValue = "on") 를 추가하자
- 환경 정보가 memory=on 이라는 조건에 맞으면 동작하고, 그렇지 않으면 동작하지 않는다.
- 우리가 앞서 만든 기능과 동일하다.
@ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {};
String prefix() default "";
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}
- @ConditionalOnProperty 도 우리가 만든 것과 동일하게 내부에는 @Conditional 을 사용한다. 그리고 그 안에 Condition 인터페이스를 구현한 OnPropertyCondition 를 가지고 있다
@ConditionalOnXxx
스프링은 @Conditional과 관련해서 개발자가 편리하게 사용할 수 있도록 수많은@ConditionalOnXxx를 제공한다.
Core Features
Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use a variety of external configuration sources including Java properties files, YAML files, environment variables, a
docs.spring.io
| 애노테이션 | 설명 |
|
@ConditionalOnClass , @ConditionalOnMissingClass
|
클래스가 있는 경우 동작한다. 나머지는 그 반대
|
|
@ConditionalOnBean , @ConditionalOnMissingBean
|
빈이 등록되어 있는 경우 동작한다. 나머지는 그 반대
|
|
@ConditionalOnProperty
|
환경 정보가 있는 경우 동작한다.
|
|
@ConditionalOnResource
|
리소스가 있는 경우 동작한다.
|
|
@ConditionalOnWebApplication , @ConditionalOnNotWebApplication
|
웹 애플리케이션인 경우 동작한다.
|
|
@ConditionalOnExpression
|
SpEL 표현식에 만족하는 경우 동작한다.
|
- 이름이 직관적이어서 바로 이해가 될 것이다. @ConditionalOnXxx 는 주로 스프링 부트 자동 구성에 사용된다.
- 다음 자동 구성 클래스들을 열어서 소스 코드를 확인해보면 @ConditionalOnXxx 가 아주 많이 사용되는 것을 확인할 수 있다.
- JdbcTemplateAutoConfiguration
- DataSourceTransactionManagerAutoConfiguration
- DataSourceAutoConfiguration
참고
@Conditional 자체는 스프링 부트가 아니라 스프링 프레임워크의 기능이다. 스프링 부트는 이 기능을 확장해서 @ConditionalOnXxx를 제공한다.
'스프링 부트(핵심 원리와 활용)' 카테고리의 다른 글
| Ch04. 자동 구성(Auto Configuration) - 순수 라이브러리 사용하기 (0) | 2023.03.10 |
|---|---|
| Ch04. 자동 구성(Auto Configuration) - 순수 라이브러리 만들기 (0) | 2023.03.10 |
| Ch04. 자동 구성(Auto Configuration) - 자동 구성 직접 만들기(기반 예제) (0) | 2023.03.09 |
| Ch04. 자동 구성(Auto Configuration) - 스프링 부트의 자동 구성 (0) | 2023.03.09 |
| Ch04. 자동 구성(Auto Configuration) - 자동 구성 확인 (0) | 2023.03.09 |