Search

스프링 @Conditional

개론

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
복사
@ConditionalCondition이라는 클래스를 받고 있는데, 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 이라는 프로퍼티 값을 이용해 빈 등록 여부를 결정해보자.
(이 블로그에서 소개하는 ConditionalExpression 을 조금 더 개선한 버전..)
@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 이 일치하는 경우에만) 빈 등록이 가능하다.