개론
Conditional은 springframework의 기능으로, 특정 Bean을 조건부로 등록할 수 있게 해준다.
모듈 : spring-context
패키지 : org.springframework.context.annotation
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
Java
복사
@Conditional 은 Condition이라는 클래스를 받고 있는데, Condition 에서 빈이 등록될 조건을 상세히 정의할 수 있다.
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Java
복사
예시
대표적으로 @Profile 을 살펴보면 다음과 같다.
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
Java
복사
@Profile 을 붙이면 ProfileCondition 로직에 의해 해당 bean이 뜰지 말지 결정되는데,
class ProfileCondition implements Condition {
@Override
@SuppressWarnings("NullAway")
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().matchesProfiles((String[]) value)) {
return true;
}
}
return false;
}
return true;
}
}
Java
복사
ProfileCondition 은 @Profile("dev") 처럼 Profile annotation의 주어진 값을 읽어 적절히 파싱한 후 현재의 Environment profile과 비교하게 된다.
응용
이를 적절히 응용하면 Custom Conditional 어노테이션을 만들 수도 있다.
spring.application.name 이라는 프로퍼티 값을 이용해 빈 등록 여부를 결정해보자.
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Conditional(OnApplicationNameCondition::class)
annotation class ConditionalOnName(
val value: Array<String> = [] // 허용된 application name 목록
)
Kotlin
복사
class OnApplicationNameCondition : Condition {
override fun matches(context: ConditionContext, metadata: AnnotatedTypeMetadata): Boolean {
val attributes = metadata.getAnnotationAttributes(ConditionalOnName::class.java.name)
?: return false
val expectedNames = attributes["value"] as? Array<*> ?: return false
val actualAppName = context.environment.getProperty("spring.application.name")
return actualAppName != null && expectedNames.any { it == actualAppName }
}
}
Kotlin
복사
이렇게 custom Conditional 어노테이션과 Condition 클래스를 구현한 다음
@ConditionalOnName(value = ["consumer"])
class AService
Kotlin
복사
위 코드 처럼 어노테이션을 붙여 주면 특정 endpoint 모듈이 실행될 때만 (정확히는 spring.application.name 이 일치하는 경우에만) 빈 등록이 가능하다.