Chapter 6. 컴포넌트 스캔
컴포넌트 스캔과 의존관계 자동 주입 시작하기
AppConfig와 같이 설정 정보를 통해 빈을 등록 할 수도 있었지만 등록해야 할 스프링 빈이 수십, 수백개가 되면 관리가 귀찮고 커지게 된다.
스프링에서 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공함.
@ComponentScan
- @Component가 붙은 클래스들을 찾아 스프링 빈으로 등록함.
- AppConfig와 다르게 @Bean으로 클래스를 등록하지 않음.
- @Configuration이 붙은 컴포넌트도 자동으로 빈으로 모두 들어가게 됨으로 Filter로 지금은 테스트 예제들 제거 한 상태
@Configuration
// 기존 예제 코드를 살리기 위해서 Filter 사용해서 @Configuration 어노테이션이 붙은 걸 필터함.
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
// Bean으로 자동 등록됨
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
// 자동 의존성 주입을 해주기 위해서 붙여줌 - 현재 MemoryRepository가 들어가게 됨
// MemoryMemberRepository에 @Component 어노테이션을 붙여줬기 때문에 Bean으로 등록됨. 타입 비교 후에 자동 주입 됨.
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
의존 관계 주입도 해당 클래스에서 정의 해야 한다.
생성자 상단에 @Autowired를 선언해주면 생성 될 때 자동으로 스프링에서 의존성 주입을 해줌.
로그를 통해서 확인 할 수 있음
19:16:00.320 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoAppConfig'
19:16:00.335 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'rateDiscountPolicy'
19:16:00.336 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberServiceImpl'
19:16:00.352 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memoryMemberRepository'
19:16:00.353 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'memberServiceImpl' via constructor to bean named 'memoryMemberRepository'
19:16:00.353 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
19:16:00.361 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
19:16:00.361 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'
@ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록함.
빈 기본 이름: 클래스명 (앞 글자만 소문자) MemberServiceImpl -> memberServiceImpl / @Component("빈 이름")
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
// 스프링 컨테이너에서 타입이 같은 빈을 찾아서 주입함. getBean(MemberRepository.class)와 거의 동일함
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
AutoAppConfig를 @ComponentScan으로 지정해주는 순간 @Component가 붙은 클래스들을 자동으로 빈으로 등록함.
의존 관계 자동 주입을 위해서 @Autowired 사용.
탐색 위치와 기본 스캔 대상
// basePackage: 탐색 시작 위치 패키지를 지정 할 수 있음.
@Configuration
@ComponentScan(
basePackages = "hello.core.member",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
basePackages: 탐색할 패키지의 시작 위치 지정. (default: 지정하지 않으면 @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 됨)
basePackageClasses = 탐색할 클래스의 시작 위치 지정.
권장 방법
시작 패키지 위치를 지정하지 않고 설정 정보의 클래스의 위치는 프로젝트 최상단에 둠.
스프링 부트의 대표 시작 정보인 @SpringBootApplication과 동일한 위치에 두는 것이 관례임 - 해당 어노테이션부터 컴포넌트 스캔이 시작됨. - 부트에서는 기본적으로 사용함.
컴포넌트 스캔 기본 대상
@Component(컴포넌트 스캔) 뿐만 아니라 다음 내용도 추가로 대상에 포함됨.
@Controller: 스프링 MVC 컨트롤러로 인식
@Service: 스프링 비지니스 로직 (개발자들이 핵심 비즈니스 로직이 있다고 판단 할 수 있도록 표현)
@Repository: 스프링 데이터 접근 계층으로 인식 후 데이터 계층의 예외를 스프링 예외로 변환함.
@Configuration: 스프링 설정 정보, 스프링 빈을 싱글톤으로 유지함.
해당 어노테이션들은 모두 내부에 @Component를 포함하고 있음.
사실 애노테이션에는 상속 관계라는 것이 없어서 @Service 어노테이션에 @Component가 있다고 해서 컴포넌트 스캔 대상임을 자바 언어에서 지원하는 기능으로 알게 되는 것이 아니라, 스프링에서 지원하게 되는 기능임.
필터
IncludeFilters: 컴포넌트 스캔 대상을 추가로 지정 - @Component를 붙이면 추가 되는 거기 때문에 (기본으로 제공) 잘 사용하지 않음. inclueFilter에 넣은 클래스들에 @Component를 붙이는 것과 동일하다고 보면 됨.
ExcludeFilters: 컴포넌트 스캔에서 제외할 대상 지정 - 간혹 사용
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class))
static class ComponentFilterAppConfig {
}
FilterType 5가지
ANNOTATION
ASSIGNABLE
ASPECTJ
REGEX
CUSTOM
중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하게 되면?
1. 자동 빈 등록 vs 자동 빈 등록
컴포넌트 스캔에 의해서 자동으로 등록 될 때는 스프링에서 오류(ConflictingBeanDefinitionException
)를 터뜨림.
2. 수동 빈 등록 vs 자동 빈 등록
20:49:50.584 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing [Generic bean: class [hello.demo.member.MemoryMemberRepository]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodNames=null; destroyMethodNames=null; defined in file [....hello\demo\member\MemoryMemberRepository.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=autoAppConfig; factoryMethodName=memberRepository; initMethodNames=null; destroyMethodNames=[(inferred)]; defined in hello.demo.AutoAppConfig]
수동 빈 등록이 우선권을 가진다.
하지만 동작 할 때 오류 나도록 기본 값이 설정되어 있음 - 명확하지 않기 때문에.
Q. 테스트 코드에서는 오류가 나지 않는데 CoreApplication 실행 시는 오류가 나는 이유?
basicScan() 테스트 코드의 경우 순수한 스프링 프레임워크. - new AnnotationConfigApplicationContext()
CoreApplication은 스프링 부트 프레임 워크를 사용해서 실행하는 것. 부트도 결국 스프링 프레임 워크여서 결과가 같아야 하지만 스프링 프레임워크의 몇가지 옵션들을 수정해서 사용하기 때문에 차이가 나게 됨.
'Spring' 카테고리의 다른 글
스프링 핵심 원리 기본편 - 챕터 8. 빈 생명주기 콜백 (0) | 2023.03.26 |
---|---|
스프링 핵심 원리 기본편 - 챕터 7. 의존관계 자동 주입 (0) | 2023.03.26 |
스프링 핵심 원리 기본편 - 챕터 5. 싱글톤 컨테이너 (0) | 2023.03.19 |
스프링 핵심 원리 기본편 - 챕터 4 (0) | 2023.03.12 |
스프링 핵심 원리 기본편 - 챕터 3 (0) | 2023.03.10 |
댓글