📌 자동 주입과 함께 사용하는 추가 기능이 컴포넌트 스캔이다. 컴포넌트 스캔은 스프링이 직접 클래스를 검색해서 빈으로 등록해주는 기능이다. 설정 클래스에 빈으로 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있으므로 컴포넌트 스캔 기능을 사용하면 설정 코드가 크게 줄어든다.
@Component 애노테이션으로 스캔 대상 지정
@Component
@Component("infoPrinter")
@Component 애노테이션에 값을 주었는지에 따라 빈으로 등록할 때 사용할 이름이 결정된다.
값을 주지 않을 때에는?
@Component 애노테이션이 붙은 클래스 이름을 따와 빈 이름으로 사용한다. 이때 첫 글자를 소문자로 바꾼 이름을 사용한다. ex) MainClass → mainClass
@ComponentScan 애노테이션으로 스캔 설정
@ComponentScan(basePackages = {"spring"}) // 패키지에 속한 클래스를 스캔 대상으로 설정
//component애노테이션이 붙은 클래스의 객체를 생성해서 빈으로 등록
@ComponentScan(basePackages = {"org.example.chap05.spring"}) // 패키지 풀네임을 적어야함
@Component 애노테이션을 붙인 클래스를 스캔해서 스프링 빈으로 등록하려면 설정 클래스에 @ComponentScan 애노테이션을 적용해야 한다.
스프링 컨테이너가 @Component 애노테이션을 붙인 클래스를 검색 해서 빈으로 등록 해주기 때문에 설정 코드가 줄어들 수 있다.
스캔 대상에서 제외하거나 포함하기
excludeFilters 속성을 사용하면 스캔할 때 특정 대상을 자동 등록 대상에서 제외할 수 있다.
정규식(FilterType.REGEX)
@ComponentScan(basePackages = {"spring" },
excludeFilters = @Filter(type = FilteType.REGEX, pattern = "spring\\\\..*Dao"))
// spring. 으로 시작하고 Dao로 끝나는 클래스를 스캔대상에서 제외한다.
정규표현식을 사용해서 제외 대상을 지정한다는 것을 의미한다.
AspectJ 패턴(FilterType.ASPECTJ)
@ComponentScan(basePackages = {"spring" },
excludeFilters = @Filter(type = FilterType.ASPECTJ, pattern = "spring.*Dao"))
// spring 패키지 에서 이름이 Dao로 끝나는 타입을 컴포넌트 스캔 대상에서 제외한다.
"spring.*Dao" AspectJ 패턴은 spring 패키지의 Dao로 끝나는 타입을 지정한다는 정도로만 알고 넘어가자.
AspectJ 패턴이 동작하려면 의존 대상에 aspectjweaver 모듈을 추가해야 한다.
특정 애노테이션을 붙인 타입(FilterType.ANNOTATION)
@Retention(RUNTIME)
@Target(TYPE)
public @interface NoProduct {
}
@Retention (RUNTIME)
@Target(TYPE)
public @interface ManualBean {
}
@ComponentScan(basePackages = {"spring", "spring2"},
excludeFilters = @Filter(type = FilterType.ANNOTATION,
classes = {NoProduct.class, ManualBean.class } ))
// @NoProduct, @ManualBean 애노테이션이 붙은 클래스를 컴포넌트 스캔 대상에서 제외한다.
@ManualBean
@Component
public class MemberDao {
}
클래스 기준(FilterType.ASSIGNABLE_TYPE)
@ComponentScan(basePackages = { 11spring11 },
excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE,
classes = MemberDao.class ))
classes에 할당할 수 있는 클래스, 즉 상속이나 구현한 클래스까지 포함한다.
필터가 2개 이상일 때
@ComponentScan의 excludeFilters 속성에 배열을 사용해서 @Filter 목록을 전달하면 된다.
@ComponentScan(basePackages = {"spring"},
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = ManualBean.class ),
@Filter(type = FilterType.REGEX, pattern = "spring2\\\\ ..*")
})
기본 스캔 대상
@Component 애노테이션을 붙인 클래스만 컴포넌트 스캔 대상에 포함되는 것은 아니다. 다음 애노테이션을 붙인 클래스가 컴포넌트 스캔 대상에 포함된다.
- @Component(org . springlramework . stereotype 패키지)
- @Controller(org . springlramework . stereotype 패키지)
- @Service(org . springlramework . stereotype 패키지)
- @Repository(org . springlramework . stereotype 패키지)
- @Aspect(org . aspectj .lang . annotation 패키지)
- @Configuration(org . springframework . context . annotation 패키지)
@Aspect 애노테이션을 제외한 나머지 애노테이션은 실제로는 @Component 애노테이션에 대한 특수 애노테이션이다.(지금은 가독성에 따른다고 생각하자)
컴포넌트 스캔에 따른 충돌 처리
빈 이름 충돌
각각 다른 패키지에 같은 이름의 빈이 등록되어 있다. 이는 빈 충돌을 발생 시킨다.
ConflictingBeanDefinitionException
서로 다른 타입인데 같은 빈 이름을 사용하는 경우가 있다면 둘 중 하나에 명시적으로 빈 이름을 지정해서 이름 충돌을 피해야 한다.
수동 등록한 빈과 충돌
@Component
public class MemberDao {
}
@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx {
@Bean
public MemberDao memberDaoO {
MemberDao memberDao = new MemberDao() ;
return memberDao:
}
스캔할 때 사용하는 빈 이름과 수동 등록한 빈 이름이 같은 경우 수동 등록한 빈이 우선한다. 즉 MemberDao 타입 반은 AppCtx에서 정의한 한 개만 존재한다.
@Configuration
@ComponentScan(basePackages = {"spring"})
public class AppCtx {
@Bean
public MemberDao memberDao2() {
}
이 경우 스캔을 통해 등록한 "memberDao" 빈과 수동 등록한 "memberDao2" 빈이 모두 존재한다. MemberDao 타입의 빈이 두 개가 생성되므로 자동 주입하는 코드는 @Qualifier 애노테이션을 사용해서 알맞은 빈을 선택해야 한다.
'Spring > 스프링5 프로그래밍 입문' 카테고리의 다른 글
Chapter7. AOP 프로그래밍 (0) | 2023.02.22 |
---|---|
Chapter6. 빈 라이프사이클과 범위 (0) | 2023.02.21 |
Chapter4. 의존 자동 주입 (0) | 2023.02.20 |
Chapter3. 스프링DI (0) | 2023.02.19 |
Chapter2. 스프링 시작하기 (0) | 2023.02.18 |