728x90
컴포넌트 스캔과 의존관계 자동 주입 시작하기
AutoAppConfig.java
@Configuration
@ComponentScan(
//Configuration 빼주는 이유는 AppConfig에 붙어있기 때문 - 예제를 실행하기 위해서 설정한 것
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
- 구현체들에 붙여주면 된다.
@Component
public class MemoryMemberRepository implements MemberRepository{
@Component
public class RateDiscountPolicy implements DiscountPolicy{
@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Autowired
//AppConfig처럼 생성자에 파라미터를 넣어주는 파일이 없기 때문에
//생성자에 @Autowired를 붙여주면 스프링이 MemberRepository 타입에 맞는 애들 찾아와서
//자동으로 연결해서 주입해 줌
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
자동으로 빈이 등록되기 때문에 의존관계를 설정할 방법이 없다 따라서 @Autowired를 사용해서 이 의존관계를 주입해주도록 해주는 것
@Component
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Autowired 를 사용하면 생성자에서 여러 의존관계도 한번에 주입받을 수 있다.
AutoAppConfigTest.java
public class AutoAppConfigTest {
@Test
void basicScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
컴포넌트 스캔과 자동 의존관계 주입이 어떻게 동작하는지
탐색 위치와 기본 스캔 대상
만약 위의 코드에 basePackges = "hello.core.member" 를 해준다면 member만 컴포넌트 스캔의 대상이 된다.
권장하는 방법
개인적으로 즐겨 사용하는 방법은 패키지 위치를 지정하지 않고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 것이다. 최근 스프링 부트도 이 방법을 기본으로 제공한다.
위의 어노테이션들이 스캔 대상이 되는 이유는 소스 코드를 보면 다 @Component가 붙어있기 때문
필터
@MyIncludeComponent
public class BeanA {
}
@MyExcludeComponent
public class BeanB {
}
public class ComponentFilterAppConfigTest {
@Test
void filterScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
//@MyIncludeComponent가 붙은 BeanA 클래스 가져옴
BeanA beanA = ac.getBean("beanA", BeanA.class);
Assertions.assertThat(beanA).isNotNull();
//예외발생 - @MyIncludeComponent가 아니라서 스캔 대상에서 빠짐
//NoSuchBeanDefinitionException가 발생한다면 성공
org.junit.jupiter.api.Assertions.assertThrows(
NoSuchBeanDefinitionException.class,
()->ac.getBean("beanB", BeanA.class)
);
}
@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig{
}
}
BeanA도 빼고 싶으면 아래와 같이 추가
@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = BeanA.class)
}
)
static class ComponentFilterAppConfig{
}
잘 사용하는 일은 없음
중복 등록과 충돌
이런 경우는 쉽게 고칠 수 있고, 일어날 일도 거의 존재하지 않는다.
@Component //memoryMemberRepository 이런 식으로 빈 이름이 자동으로 변경됨
public class MemoryMemberRepository implements MemberRepository{
MemoryMemberRepository의 빈 이름과 똑같은 이름의 빈을 AutoAppConfig에 생성
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
MemoryMemberRepository memberRepository(){
return new MemoryMemberRepository();
}
테스트를 실행해보면 오류 없이 잘 실행되는 것을 볼 수 있는데, 이 경우 수동 빈 등록이 우선권을 가지게 되었기 때문에 그렇다.
(수동 빈이 자동 빈을 오버라이딩 해버린다.)
스프링 부트인 CoreApplication 을 실행해보면 오류를 볼 수 있다.
728x90
'스프링 > 기본편' 카테고리의 다른 글
빈 생명주기 콜백 (0) | 2022.05.23 |
---|---|
의존관계 자동 주입 (0) | 2022.05.22 |
싱글톤 컨테이너 (0) | 2022.05.22 |
스프링 컨테이너와 스프링 빈 (0) | 2022.05.22 |
스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2022.05.21 |