[Spring] 컴포넌트 스캔(ComponentScan)

Date:     Last Updated:

컴포넌트 스캔

  • 컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다.
  • 원래라면 설정클래스(AppCtx, Config)에서 Bean으로 등록을 해야 스프링 컨테이너가 해당 클래스를 알 수 있는데 컴포넌트 스캔을 하면 자동으로 등록하기 때문에 설정 코드가 크게 줄어든다.

컴포넌트 스캔 설정 @ComponentScan

  • 스프링에서는 @ComponentScan 어노테이션을 가진 설정 클래스가 존재해야 검색을 할 수 있지만 스프링부트는 생성시 만들어지는 @SpringBootApplication 로 내부적으로 @ComponentScan을 가지고 있어서 따로 등록할 필요는 없다.
  • 스캔을 사용하면 @Component를 가진 클래스들을 자동으로 등록해주고 범위를 지정할 수 있다.
  • 범위 지정 방법은 @ComponentScan(basePackages = {"spring"}) 이렇게 하면 spring 패키지와 그 하위 해키지에 속한 클래스를 스캔 대상으로 설정한다.

스캔 대상에서 제외, 포함하기

1. FilterType.REGEX

  • @Filter 어노테이션을 이용해서 정규표현식으로 제외할 목록을 지정할 수 있다.
  • 아래 코드는 spring으로 시작하고 Dao로 끝나는 컴포넌트를 스캔대상에서 제외한다.
    1
    
    @ComponentScan(basePackages = {"spring"}, excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao"))
    


2. FilterType.ANNOTATION

  • 또는 특정 어노테이션을 붙인 클래스를 제외할 수도 있다.
  • 아래 코드는 @NoProduct나 @ManualBean 어노테이션을 붙인 클래스를 제외한다.
1
@ComponentScan(basePackages = {"spring"}, excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = {NoProduct.class, ManualBean.class}))


3. FilterType.ASSIGNABLE_TYPE

  • 특정 타입이나 그 하위 타입을 컴포넌트 스캔 대상에서 제외하려면 아래 코드처럼 ASSIGNABLE_TYPE을 filterType으로 사용한다.
    1
    
    @ComponentScan(basePackages = {"spring"}, excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MemberDao.class))
    


4. 여러개의 필터가 필요할때

  • 설정할 필터가 두개 이상이면 배열을 사용한다.
    1
    2
    3
    4
    
    @ComponentScan(basePackages = {"spring"}, excludeFilters = {
    @Filter(type = FilterType.ANNOTATION, classes = ManualBean.class),
    @Filter(type = FilterType.REGEX, pattern = "spring3\\..*")
    })
    

기본 스캔 어노테이션

  • @Component, @Controller, @Service, @Repository, @Configuration, @Aspect
  • 이 중에서 Aspect를 제외한 나머지 5개는 모두 내부적으로 @Component 어노테이션을 가지고 있다.

컴포넌트 스캔시 충돌 처리

1. 자동 등록한 2개의 패키지 범위 충돌

  • 컴포넌트 스캔을 이용해서 자동으로 빈을 등록할 때는 충돌에 주의해야 한다.
  • 아래 코드와 같이 2개의 패키지를 컴포넌트 스캔 범위로 설정했는데 둘다 같은 이름의 클래스가 있다면 충돌이나서 예외가 발생하게 된다.
  • 그러므로 같은 이름을 피하거나, 둘 중 하나에 명시적으로 빈 이름을 지정해서 충돌을 피해야 한다.
    1
    
    @ComponentScan(basePackages = {"spring", "spring2})
    

2. 수동 등록한 빈과 충돌

  • MemberDao에 @Component 어노테이션을 붙여서 컴포넌트 스캔 대상이 되었다.
    1
    2
    3
    4
    
    @Component
    public class MemberDao {
    ...
    }
    


  • 만약 설정에서 아래와 같이 수동으로 Bean을 등록하면 어떻게 될까?
  • 이때는 수동으로 등록한 Bean이 우선이 된다.
1
2
3
4
5
@Bean
public MemberDao memberDao(){
  MemberDao memberDao = new MemberDao();
  return memberDao;
}

마무리

  • 스프링부트도 사용해본 입장에서 확실히 컴포넌트 스캔이라는 것은 좋은 방법이다.
  • 다만 개인적으로는 프로그래밍에서 자동으로 되는 것은 단 한개도 없다! 라는 생각을 가지고 있기 때문에, 쉽게 사용하는 설정이나 라이브러리, 프레임워크 등은 누군가가 고생해서 만들어 놓은 부분이라고 생각한다.
  • 그리고 그것을 더 잘 쓰기 위해서는 어느 정도는 내부적으로 어떻게 동작하는 지 알아둘 필요가 있다고 생각해서 스프링부트를 쓰면 더 간단하지만 스프링이 어떻게 동작하는지 알아가는 것도 중요하다고 생각된다.

Leave a comment