꼬물꼬물
컴포넌트 스캔 [필터, 중복 등록과 충돌] 본문
필터
@ComponentScan 하위
- includeFilters: 컴포넌트 스캔 대상 추가 지정
- excludeFilters: 컴포넌트 스캔 제외 대상 지정
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyincludeComponent {
}
1. @ 어노테이션 생성
@MyIncludeComponent
public class BeanA {
}
@MyExcludeComponent
public class BeanB {
}
2. 어노테이션을 갖는 클래스 생성
public class ComponentFilterAppConfigTest {
@Test
void filterScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
Assertions.assertThat(ac.getBean("beanA")).isNotNull();
assertThrows(NoSuchBeanDefinitionException.class, () ->ac.getBean("beanB"));
}
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class))
static class ComponentFilterAppConfig{}
}
3. Filter 사용해 스캔 대상, 제외 대상 지정
- includeFilters에 MyIncludeComponent 어노테이션을 추가해 BeanA가 스프링 빈에 등록
- excludeFilters에 MyExcludeComponent 어노테이션을 추가해 BeanB가 스프링 빈에 제외
FilterType 옵션
- FilterType.ANNOTATION: 기본값, 어노테이션을 인식해 동작 (org.example.SomeAnnotation)
- ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해 동작 (org.example.SomeClass) // 클래스 지정
- ASPECTJ: AspectJ 패턴 사용 (org.example..*Service+)
- REGEX: 정규표현식 (org\.example\.Defailt.*)
- CUSTOM: TypeFilter 이라는 인터페이스를 구현해 처리 (org.example.MyTypeFIlter)
EX) BeanA도 빼고싶다면?
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class)) // 추가 부분
static class ComponentFilterAppConfig{}
@Component로 충분하기 때문에, includeFilters를 사용할 일은 거의 없다. excludeFilters는 여러가지 이유로 간혹 사용하지만 많지는 않음.
특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다는 스프링 기본 설정에 맞춰 사용하는 것을 권장.
중복 등록과 충돌
💡 컴포넌트 스캔에서 같은 빈 이름이 등록되면 어떻게 될까?
- 자동 빈 등록 vs 자동 빈 등록
- 수동 빈 등록 vs 자동 빈 등록
자동 빈 등록 VS 자동 빈 등록
- 컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록되는데 그 이름이 같은 경우 스프링은 오류를 발생
- ConflictingBeanDefinitionException 예외 발생
수동 빈 등록 VS 자동 빈 등록
// 수동
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
MemoryMemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
// 자동
@Component // 빈 등록 네임은 memoryMemberRepository
public class MemoryMemberRepository implements MemberRepository{
}
- 이 경우 수동 빈 등록이 우선권을 가진다.
- 수동 빈인 자동 빈을 오버라이딩 한다.
🚨 현실은 개발자가 의도적으로 설정했기보다 설정이 꼬여서 이런 결과가 만들어진다.
- 최근 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생하도록 기본 값을 바꾸었다.
- 스프링 부트(@SpringBootApplication)로 실행하면 오류가 난다.
'스터디 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
의존관계 자동 주입 [옵션 처리, 생성자 주입] (0) | 2022.09.28 |
---|---|
의존관계 자동 주입 [다양한 의존관계 주입 방법] (0) | 2022.09.25 |
컴포넌트 스캔 [의존관계 자동 주입, 탐색 위치와 기본 스캔 대상] (0) | 2022.09.19 |
싱글톤 컨테이너 [@Configuration] (0) | 2022.09.18 |
싱글톤 컨테이너 [싱글톤 컨테이너] (0) | 2022.09.17 |