[Spring] Component Scan

jckim22·2023년 12월 2일
0

[SPRING] STUDY

목록 보기
18/57
@Component
public class MemberServiceImpl implements MemberService{
@Component
public class MemoryMemberRepository  implements MemberRepository{
@Component
public class OrderServiceImpl implements OrderService{

위처럼 컴포넌트로 등록해놓으면 스프링이 컴포넌트 스캔을 진행해서 스프링 빈 저장소에 넣어준다.

@ComponentScan이라는 어노테이션이 붙은 설정파일의 패키지부터 그 하위패키지 전부를 스캔하게 되는데 사실 @SpringBootAplication을 통해 ComponentScan까지 진행되기 때문에 우리는 스프링만 잘 사용하면 된다.

근데 의존관계를 어떻게 주입할 수 있을까?

    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

위와 같이 @Autowired로 DI를 진행하면 된다.
Autowired를 통해 해당 컴포넌트를 스프링 빈 저장소에서 찾아서 주입해준다.

구조는 아래와 같다.



위 단계들을 거쳐서 자동으로 의존관계 주입이 되고 싱글톤도 지키게 된다.

필터

@MyIncludeComponent
public class BeanB {
}

@MyExcludeComponent
public class BeanA {
}
    @Test
    void filterScan() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
        BeanA beanA = ac.getBean("beanA", BeanA.class);
        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 {

    }

위 코드를 보면 @ComponentScan에서 include, excludeFilters를 사용해 제외할 컴포넌트를 고른다.
여기서 컴포넌트 어노테이션은 아래처럼 직접 제작했다.

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

@ExcludeComponet로 등록된 객체는 스프링 빈에 등록되지 않는다.

FilterType 옵션 FilterType은 5가지 옵션이 있다.

  • 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

참고: @Component 면 충분하기 때문에, includeFilters 를 사용할 일은 거의 없다. excludeFilters 는 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않다.
특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다 는 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장하고, 선호하는 편이다.

중복 등록과 충돌

자동 등록 vs 자동 등록

이 경우에는 ConflictingBeanDefinitionException 예외가 발생된다.

자동 등록 vs 수동 등록

이 경우에는 수동 등록이 우선 순위를 갖게 된다.

스프링 부트의 대처

물론 개발자가 의도적으로 이런 결과를 기대했다면, 자동 보다는 수동이 우선권을 가지는 것이 좋다. 하지만 현실은 개 발자가 의도적으로 설정해서 이런 결과가 만들어지기 보다는 여러 설정들이 꼬여서 이런 결과가 만들어지는 경우가 대 부분이다!

최근 스프링 부트에서는 자동 수동의 상황에서도 에러를 내도록 바뀌었다.

profile
개발/보안

0개의 댓글