Spring/Spring core basic

[Spring core basic] 30 - 컴포넌트 스캔 필터링

Kloong 2022. 5. 2. 01:31

참고

더보기

Spring core basic 시리즈는 김영한 님의 "스프링 핵심 원리 - 기본편" 강의를 정리한 글입니다. 글에 첨부된 사진은 해당 강의의 강의 자료에서 캡쳐한 것입니다. 제 Github에만 올려뒀다가, 정보 공유와 강의 홍보(?)를 위해 블로그에도 업로드합니다. 마크다운을 잘 쓰지 못해서 가독성이 조금 떨어지는 점 양해 바랍니다.

 

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com

컴포넌트 스캔 필터링

특정 대상을 컴포넌트 스캔 대상에 추가하거나, 제외할 수 있다.

  • includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.
  • excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다.

임의의 annotation을 만들어서, 해당 annotation이 붙으면 컴포넌트 스캔 대상에 추가되거나 제외되게끔 해보자.

MyIncludeComponent.java

package com.kloong.corebasic1.scan.filter;

import java.lang.annotation.*;

@Target(ElementType.TYPE) //클래스 레벨에 붙는 annotation
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent { }

MyExcludeComponent.java

package com.kloong.corebasic1.scan.filter;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent { }

두 annotation에 붙은 3개의 annotation은 @Component annotation에도 동일하게 존재한다.

Component.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component { /* 생략 */ }

이제 @MyIncludeComponent 가 붙으면 컴포넌트 스캔 대상에 추가되고, @MyExcludeComponent 가 붙으면 컴포넌트 스캔에서 제외되게끔 설정해보자.

BeanA.java

package com.kloong.corebasic1.scan.filter;

@MyIncludeComponent
public class BeanA {
}

@MyIncludeComponent annotation을 붙였다.

BeanB.java

package com.kloong.corebasic1.scan.filter;

@MyExcludeComponent
public class BeanB {
}

@MyExcludeComponent annotation을 붙였다.

ComponentFilterAppConfigTest.java

//package, import 생략

public class ComponentFilterAppConfigTest {

    @Test
    void filterScan() {
        ApplicationContext ac =
        new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);

        BeanA beanA = ac.getBean("beanA", BeanA.class);

        Assertions.assertThat(beanA).isNotNull();
        assertThrows(NoSuchBeanDefinitionException.class,
                () -> ac.getBean("beanB", BeanB.class));
    }

    @Configuration
    @ComponentScan(
            includeFilters =
            @ComponentScan.Filter(type = FilterType.ANNOTATION,
            classes = MyIncludeComponent.class),

            excludeFilters =
            @ComponentScan.Filter(type = FilterType.ANNOTATION,
            classes = MyExcludeComponent.class)
    )
    static class ComponentFilterAppConfig {
    }
}
  • includeFilters 에 MyIncludeComponent annotation을 추가해서 BeanA가 스프링 빈에 등록된다.
  • excludeFilters 에 MyExcludeComponent annotation을 추가해서 BeanB는 스프링 빈에 등록되지 않는다.

BeanB가 빈으로 등록되지 않았기 때문에, ac.getBean("beanB", BeanB.class) 를 하는 순간 NoSuchBeanDefinitionException이 발생한다.

FilterType 옵션

  • ANNOTATION: 기본값, 애노테이션을 인식해서 동작한다.
    • 기본값이기 때문에, 위에서 작성한 코드에서 생략해도 잘 동작한다.
    • Ex) org.example.SomeAnnotation
  • ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해서 동작한다.
    • Ex) org.example.SomeClass
  • ASPECTJ: AspectJ 패턴 사용
    • Ex) org.example..*Service+
  • REGEX: 정규 표현식
    • Ex) org\.example\.Default.*
  • CUSTOM: TypeFilter 이라는 인터페이스를 구현해서 처리
    • Ex) org.example.MyTypeFilter

@MyIncludeComponent 가 붙은 BeanA를 컴포넌트 스캔 대상에서 제외하고 싶다면, ASSIGNABLE_TYPE 을 사용해서 다음과 같이 해주면 된다.

ComponentFilterAppConfigTest.java

//package, import 생략

public class ComponentFilterAppConfigTest {

   //테스트 메소드 생략

    @Configuration
    @ComponentScan(
            includeFilters =
            @ComponentScan.Filter(type = FilterType.ANNOTATION,
            classes = MyIncludeComponent.class),

            excludeFilters =
            @ComponentScan.Filter(type = FilterType.ANNOTATION,
            classes = MyExcludeComponent.class)
            excludeFilters =
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
            classes = BeanA.class) //BeanA 제외
    )
    static class ComponentFilterAppConfig {
    }
}

*참고: @Component 면 충분하기 때문에, includeFilters 를 사용할 일은 거의 없다. excludeFilters 는 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않다.

특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다는 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장하고, 선호하는 편이다.