[Spring core basic] 30 - 컴포넌트 스캔 필터링
참고
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
- Ex)
- ASPECTJ: AspectJ 패턴 사용
- Ex)
org.example..*Service+
- Ex)
- REGEX: 정규 표현식
- Ex)
org\.example\.Default.*
- Ex)
- CUSTOM: TypeFilter 이라는 인터페이스를 구현해서 처리
- Ex)
org.example.MyTypeFilter
- Ex)
@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
는 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않다.특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다는 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장하고, 선호하는 편이다.