컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다. 설정 클래스에 빈으로 등록하지 않아도 클래스를 빈으로 등록해주는 기능이다. 설정 클래스에 빈으로 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있으므로 컴포넌트 스캔 기능을 사용하면 설정 코드가 크게 줄어든다.
스프링이 검색해서 빈으로 등록할 수 있으려면 클래스에 @Component 어노테이션을 붙여야 한다. @Component 어노테이션은 해당 클래스를 스캔 대상으로 지정한다.
@Component
public class MemberDao{ ... }
@Component("member")
public class MemberDao{ ... }
@Component 어노테이션을 붙인 클래스를 스캔해서 빈으로 등록하기 위해서는 설정 클래스에 @ComponentScan 어노테이션을 설정해 줘야 한다.
// 빈으로 등록하기 위한 클래스
// MemberDao가 spring 이름으로 된 패키지에 있다고 가정
@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx{
// ... 생략
}
basePackages 속성에 빈에 등록할 패키지를 지정할 경우 해당 패키지와 그 하위 패키지에 속한 클래스를 스캔 대상으로 지정하고 @Component 어노테이션이 지정된 클래스를 빈을 등록한다.
excludeFilters 속성을 사용하면 빈으로 등록하기 위해 스캔을 할 경우 특정 대상을 자동 등록 대상에서 제외 시킬 수 있다.
@Configuration
@ComponentScan(basePackages = {"spring"},
excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao"))
public class AppCtxWithExclude{
@Bean
public MemberDao memberDao(){
}
}
@Filter 어노테이션 속성
excludeFilters = @Filter(type = FilterType.REGEX, pattern = "spring\\..*Dao")
// 정규식 표현 spring으로 시작하고 Dao로 끝나는 클래스를 스캔 대상에서 제외
excludeFilters = @Filter(type = FilterType.ASPECTJ, pattern = "spring.*Dao")
// AspectJ 표현 spring으로 시작하고 Dao로 끝나는 클래스를 스캔 대상에서 제외
excludeFilters = @Filter(type=FilterTYPE.ASSIGNABLE_TYPE, classes=MemberDao.class)
// MemberDao를 스캔 대상에서 제외
특정 어노테이션을 스캔 대상에서 제외하고 싶다면 예를 들어 @NoProduct나 @ManualBean 어노테이션을 붙인 클래스는 컴포넌트 스캔 대상에서 제외할 경우
@Retention(RUNTIME)
@Target(TYPE)
public @interface NoProduct{}
@Retention(RUNTIME)
@Target(TYPE)
public @interface ManualBean{}
@Configuration
@ComponentScan(basePackages={"spring","spring2"},
excludeFilters=@Filter(type=FilterTYPE.ANNOTATION, classes={NoProduct.class,ManualBean.class}))
public class AppCtxWithExclude{...}excludeFilters=@Filter(type=FilterTYPE.ANNOTATION, classes={NoProduct.class,ManualBean.class}))
@ManualBean
@Component
public class MemberDao{...}
@ManualBean 이 붙은 어노테이션을 스캔 대상에서 제외된다.
만약 설정할 필터가 두 개 이상이면 @ComponentScan의 excludeFilters 속성에 배열을 사용해서 @Filter 목록을 전달하면 된다.
@Configuration
@ComponentScan(basePackages={"spring"},
excludeFilters={
@Filter(type=FilterTYPE.REGEX, classes=MemberDao.class, pattern="spring2\\..*"),
@Filter(type=FilterTYPE.REGEX, classes=MemberDao.class, classees=ManualBean.class)
})
public class AppCtxWithExclude{...}
@Component 어노테이션을 붙인 클래스만 컴포넌트 스캔 대상에 포함되는 것이 아니다. 아래 어노테이션을 붙인 클래스들은 모두 컴포넌트 스캔 대상이 된다.
이 어노테이션들은 @Component와 같은 기능을 수행한다. 이 어노테이션들은 특정 클래스에 동작 방식을 구별할 수 있게 이름으로 분리한 것 뿐이다.
컴포넌트 스캔 기능을 사용해서 자동으로 빈 등록할 때에는 충돌에 주의해야 한다. 크게 빈 이름 충돌과 수동 등록에 따른 충돌이 발생할 수 도 있다.
spring 패키지와 spring2 패키지에 MemberRegisterService 클래스가 존재하고 두 클래스 모두 @Conponent 어노테이션을 붙였다고 하자. 이 상태에서 다음 @ComponentScan 어노테이션을 사용하면 익셉션 오류가 발생한다. 두 클래스에 같은 빈 이름을 지정하게 될 경우 타입이 일치하지 않는 빈 이름과 충돌이 나기 때문이다. 같은 빈 이름을 사용할 경우 둥 중 하나에 명시적으로 빈 이름을 지정해서 이름 충돌을 피해야 한다.
만약 스캔할 때 사용하는 빈 이름과 수동 등록한 빈 이름이 같은 경우 수동 등록한 빈이 우선이기 때문에 수동 등록한 빈만 존재하게 된다.