컴포넌트 스캔
컴포넌트 스캔
컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다. 설정 클래스에 빈으로 등록하지 않아도 클래스를 빈으로 등록해주는 기능이다. 설정 클래스에 빈으로 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있으므로 컴포넌트 스캔 기능을 사용하면 설정 코드가 크게 줄어든다.
@Component 어노테이션으로 스캔 대상 지정
스프링이 검색해서 빈으로 등록할 수 있으려면 클래스에 @Component 어노테이션을 붙여야 한다. @Component 어노테이션은 해당 클래스를 스캔 대상으로 지정한다.
@Component
public class MemberDao{ ... }
- 기본적으로 @Component에 아무 속성을 지정하지 않을 경우 클래스 이름에 첫 글자를 소문자로 바꿔 빈 이름으로 등록되게 된다.
- 예) MemberDao -> memberDao
- 만약 클래스를 빈으로 등록할 때 별도의 이름으로 지정해서 빈으로 등록하고 싶을 경우 아래와 같이 설정해 주면 된다.
@Component("member")
public class MemberDao{ ... }
@Component 어노테이션으로 스캔 설정
@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 어노테이션 속성
- type : 제외 대상을 지정하기 위한 type을 지정
- FilterType.REGEX : 정규식 표현
- FilterType.ASPECTJ : AspectJ식 표현
- FilterType.ANNOTATION : 어노테이션 표현
- FilterType.ASSIGNABLE_TYPE : 특정 타입이나 그 하위 타입을 지정
- pattern : FilterType을 적용할 값
- pattren 속성은 String[] 타입이므로 배열을 이용해서 패턴을 한 개 이상 지정 할 수 있다.
- type : 제외 대상을 지정하기 위한 type을 지정
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
- @Controller
- @Service
- @Repository
- @Aspect
- @Configuration
이 어노테이션들은 @Component와 같은 기능을 수행한다. 이 어노테이션들은 특정 클래스에 동작 방식을 구별할 수 있게 이름으로 분리한 것 뿐이다.
컴포넌트 스캔에 따른 충돌 처리
컴포넌트 스캔 기능을 사용해서 자동으로 빈 등록할 때에는 충돌에 주의해야 한다. 크게 빈 이름 충돌과 수동 등록에 따른 충돌이 발생할 수 도 있다.
1. 빈 이름 충돌
spring 패키지와 spring2 패키지에 MemberRegisterService 클래스가 존재하고 두 클래스 모두 @Conponent 어노테이션을 붙였다고 하자. 이 상태에서 다음 @ComponentScan 어노테이션을 사용하면 익셉션 오류가 발생한다. 두 클래스에 같은 빈 이름을 지정하게 될 경우 타입이 일치하지 않는 빈 이름과 충돌이 나기 때문이다. **같은 빈 이름을 사용할 경우 둥 중 하나에 명시적으로 빈 이름을 지정해서 이름 충돌을 피해야 한다.**
2. 수동 등록한 빈과 충돌
만약 스캔할 때 사용하는 빈 이름과 수동 등록한 빈 이름이 같은 경우 수동 등록한 빈이 우선이기 때문에 수동 등록한 빈만 존재하게 된다.