@Conditional
특정 조건일 때만 해당 기능을 활성화 할수 있고, 소스코드를 고치지 않고 가능하다. 특정상황일때만 특정 빈들을 등록해서 사용하도록 도와주는 기능이다.
package org.springframework.context.annotation;
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
matches() 메서드가 true 를 반환하면 조건에 만족해서 동작하고, false 를 반환하면 동작하지 않는다.
ConditionContext : 스프링 컨테이너, 환경 정보등을 담고 있다.
AnnotatedTypeMetadata : 애노테이션 메타 정보를 담고 있다.
이것을 implements 하고 구현체를 만들고 @Conditional 어노테이션을 사용해 구현체를 등록 시키면 해당 클래스의 기능을
matches() 매서드에 따라 활성화 또는 비활성화 할 수 있다.
아래에 예시를 적어 놓았다.
@Conditional 예제
@Configuration
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
여기서 @Configuration 으로 서버가 실행될때 객체를 IoC 컨테이너에 Bean 으로 등록하여 사용할려고 하였다.
@Slf4j
public class MemoryCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String memory = context.getEnvironment().getProperty("memory");
log.info("memory={}", memory);
return "on".equals(memory);
}
}
Condition 을 구현체로 새로 하나 만들고 환경설정값의 memory 값을 가져와 on 인지 아닌지 확인하는 matches() 만들었다.
@Configuration
@Conditional(MemoryCondition.class)
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
그리고 추가해서 실행해보니 기능 비활성화 되어서 객체가 등록이 안되는것을 확인할 수 있었다.
실제로 활성화하는 방법은 아래에 적어 놓겠다.
Environment
스프링은 외부 설정을 추상화해서 Environment 로 통합했다. 그래서 다음과 같은 다양한 외부 환경 설정을 Environment 하나로 읽어들일 수 있다.
#VM Options
#java -Dmemory=on -jar project.jar -Dmemory=on #Program arguments
# -- 가 있으면 스프링이 환경 정보로 사용
#java -jar project.jar --memory=on --memory=on
#application.properties
#application.properties에 있으면 환경 정보로 사용
위와 같이 사용할 수 있지만 application.properties 에 직접 추가해서 활성화 하겠다.
이런식으로 사용하게 되고, 이렇게 된 경우 로그가 on 이 되면 기능이 활성화가 된다.
@Conditional 다양한 기능들
@Configuration
//@Conditional(MemoryCondition.class)
@ConditionalOnProperty(name = "memory", havingValue = "on") //MemoryCondition.class 내용과 똑같다.
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
@ConditionalOnProperty(name = "memory", havingValue = "on") 은 우리가 MemoryCondition.class 에 만들어 놨던 기능을 @ConditionalOnProperty 이 어노테이션으로 쉽게 사용 할 수 있다.
왜 그럴까? 어노테이션 안으로 들어가 보면 알 수 있다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty
위와 같이 Conditional 이 되어 있고, 아래의 코드를 마저 보자
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
List<AnnotationAttributes> allAnnotationAttributes = metadata.getAnnotations()
.stream(ConditionalOnProperty.class.getName())
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::asAnnotationAttributes).toList();
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
OnPropertyCondition class 이다. 구현체를 보게 되면 outcome 변수에 환결설정값을 가져와 for 문으로 확인하는 구문이 존재한다. 이러므로 우리가 구현했던 기능을 @ConditionalOnProperty 으로 쉽게 구현이 가능하다.
대표적인 편리한 기능들이다.
@ConditionalOnClass , @ConditionalOnMissingClass 클래스가 있는 경우 동작한다. 나머지는 그 반대 @ConditionalOnBean , @ConditionalOnMissingBean 빈이 등록되어 있는 경우 동작한다. 나머지는 그 반대 @ConditionalOnProperty 환경 정보가 있는 경우 동작한다. @ConditionalOnResource 리소스가 있는 경우 동작한다. @ConditionalOnWebApplication , @ConditionalOnNotWebApplication 웹 애플리케이션인 경우 동작한다. @ConditionalOnExpression SpEL 표현식에 만족하는 경우 동작한다.
'Spring' 카테고리의 다른 글
JPA - 구동 방식 (1) | 2023.12.27 |
---|---|
JPA - 기본 개념 (0) | 2023.12.27 |
HikariCP (0) | 2023.08.17 |
테스트 도구 Apache JMeter (0) | 2023.08.11 |
Spring interface 의존성 주입 (0) | 2023.07.31 |