Simongs NOTE

[JAVA] @Conditional

2018-01-11

@Conditional

XML 방식

  • 정적인 구조
  • 동적스캔지원 (어노테이션)
  • 커스텀 태그의 구현에 따른 일부 동적인 설정 (aop, transaction)

JavaConfig, Annotation

  • 동적인 빈 등록, 설정
  • @Bean
  • @Enable*
  • @Profile

@Enable*

  • @Configuration 단위 구성
  • @Import 활용
  • 해당 이름은 스프링 소스에서 관례적으로 붙은 이름
  • 확장방법
    • @Configuration 오버라이딩
    • @Import + Metadata/Registry
    • @Configuration + 자바코드 Configurer

@Profile

  • 빈 구성 그룹을 선택할 수 있도록 한다.
  • 환경에 따라 다르게 적용되는 빈 구성이 가능하다.

@Conditional

  • @Profile과 같이 특정 조건을 만족했을때 빈을 등록하는 기능을 일반화
  • 적용대상
    • @Configuration
    • @Bean
    • 스테레오 타입 어노테이션
  • 세밀하고 동적인 설정 구성 가능
  • Spring Boot 에서 적극적으로 사용한다.
    • Spring Boot 에 적용되었던 기술이 Spring 4 로 들어온 케이스

@Conditional 적용 구조

  • Spring은 @Conditional 의 결과를 보고 Bean 등록여부를 판단한다.
  • @Configuration Level에도 붙일수 있다. (해당 Configuration 하위의 Bean이 모두 대상이 된다.)

▶ 적용대상

@Bean
@OnFlag(true)               // 확장1 
@Conditional(MyCondition.class) // 확장1 
@MyConditional(true)        // 확장2 
@MyConditional(BeanB.class) // 확장3
public BeanA bean() {
    return new BeanA();
}

▶ 판단방법

public class MyCondition implements Condition {
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
        // 확장1. return (Boolean)metadata.getAnnotationAttributes(OnFlag.class.getName()).get("value");
        // 확장2. return (Boolean)metadata.getAnnotationAttributes(MyConditional.class.getName()).get("value");
        // 확장3. Class<?> beanClass = (Class<?>)metadata.getAnnotationAttributes(MyConditional.class.getName()).get("value");
        //        try {
        //            BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getBeanFactory(), beanClass);
        //            return true;  
        //        } catch(NoSuchBeanDefinitionException e) {
        //            return false;
        //        }
    }
}

▶ (확장1) OnFlag annotation 개발

@Retention(RetentionPolicy.RUNTIME) // 이 annotation이 언제까지 적용될 것인가? Default는 Class
public @interface OnFlag {
    boolean value();
}

▶ (확장2) MyConditional annotation 개발 (OnFlag 를 대체한다.)

@Retention(RetentionPolicy.RUNTIME) 
@Conditional(MyCondition.class) // Meta Annotation을 정의한다.
public @interface MyConditional {
    boolean value();
}

▶ (확장3) MyConditional annotation 개발 (특정 Bean이 있을때만 등록한다.)

@Retention(RetentionPolicy.RUNTIME) 
@Conditional(MyCondition.class) // Meta Annotation을 정의한다.
public @interface MyConditional {
    Class<?> value();
}

Condition Interface

▶ method code

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
     
}

▶ ConditionContext

  • BeanFactory 등등 정보를 가짐

▶ AnnotatedTypeMetadata

  • @Conditional 이 붙은 위치의 모든 annotation 정보를 가져온다.
    • 위의 예제는 @Bean, @OnFlag, @Conditional 정보를 담고 있다.
    • RetentionPolicy.RUNTIME 만 실제 값을 가져올 수 있다.

Condition 활용예제

  • @Profile
  • Spring Boot 프로젝트의 AutoConfiguration
    • OnBeanCondition : bean이 있느냐 없느냐
    • OnClassCondition : classpath에 있으냐 없느냐
    • OnExpressionCondition : SPEL 에 매칭이 되느냐
    • OnJavaCondition : Java Version 에 따라
    • OnPropertyCondition : Property Value 에 따라
    • OnResourceCondition : Resource의 존재유무
    • OnWebApplicationCondition : 현재 WEB 기반의 어플리케이션인지 판단한다.

AutoConfiguration

  • @Configuration 과 @Conditional 의 조합이다.
  • 각종 조건 및 환경에 맞는 빈 구성 및 설정 정보
  • Default Bean 구성으로 빠르게 시작해서 점진적으로 설정을 추가 확장
    • @ConditionalOnMissingBean
      • 개발자가 직접 등록한 빈이 있으면 사용하고 없으면 Default Bean을 등록한다.
  • SpringBoot의 다양한 적용방법 참고
  • @Enable* 조합

클래스 프록시 개선

  • 기존 클래스 프록시 방식의 단점
    • 클래스 생성자 2번 호출 (proxy + target)
    • 디폴트 생성자만 사용가능 (CGLib)
  • 개선된 클래스 프록시
    • 클래스 생성자 1번만 호출
    • 임의의 생성자 사용 가능
    • Objenesis와 확장된 CGLib
Reference

Comments

Content