본문 바로가기

스프링/기본편

컴포넌트 스캔

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