티스토리 뷰

Spring

컴포넌트 스캔

shield 2023. 8. 15. 15:23

 

*컴포넌트 스캔: 설정 정보가 없어도 스프링이 자동으로 스프링 빈을 등록하는 기능 

 

//구성을 스프링으로 관리하기 위한 @Configuration
@Configuration
public class AppConfig {

    //스프링 컨테이너에 객체들을 관리 하기 위해 @Bean으로 등록
    //스프링 컨테이너에 등록된 객체들을 '스프링 빈' 이라고 한다
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();

    }
}

위 코드처럼 설정 정보를 구성하면 등록해야 하는 Bean이 늘어날 때 마다 하나하나 수동으로 추가해줘야 하고 개수가 늘어날수록 누락할 확률이 높아진다. 이런 문제점을 보완하기 위해 컴포넌트 스캔을 활용한다. 아래 코드와 같다.

package hello.core;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(
//        //컴포넌트 스캔의 시작지점을 정할 수 있고, 하위 패키지도 다 포함한다
//        basePackages = "hello.core.member",
//
//        //클래스를 지정하는 방식
//        basePackageClasses = AutoAppConfig.class;

        // 만약 위 처럼 범위를 지정하지 않으면, @ComponentScan이 붙은 클래스의 패키지가 시작 위치가 된다
        // 여기서는 hello.core 부터 하위의 클래스들을 전부 스캔한다.


        //컴포넌트 스캔을 통해 @Component가 붙은 클래스를 자동으로 빈으로 등록하는데 그 중에서 배제할 것을 정하는 것.
        //만들어둔 TestConfig나 AppConfig에 @Configuration이 있는데 그것을 포함하면 거기서 수동으로 만들어
        //포함되어 있는 스프링 빈들과 충돌나기 때문에 배제
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}

 

package hello.core.member;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    //컴포넌트 스캔을 쓰게 되면 AutoAppConfig에서 의존 관계를 설정할 수 없기 때문에
    //생성자 위에 의존 관계 자동 주입을 해주는 @Autowired를 추가해준다
    //그러면 스프링 컨테이너가 자동으로 해당 클래스 타입에 해당하는 스프링 빈을 찾아서 주입한다
    @Autowired
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

-AutoAppConfig 클래스에는 설정 정보가 없는 것을 확인할 수 있다. @ComponentScan 어노테이션을 이용하면 설정된 범위에서 @Component 가 붙은 클래스를 스프링 컨테이너가 스프링 빈으로 등록한다 (참고로 @ComponentScan은 다른 어노테이션도 스캔함). 빈의 이름은 등록된 클래스 명의 앞글자만 소문자로 한 것과 같다.

 

-원래는 설정 정보에서 수동으로 new를 통해 의존관계를 주입했지만, 컴포넌트 스캔을 이용하면 설정 정보가 비어 있으므로 빈으로 등록할 클래스의 생성자에 @Autowired를 붙여서 자동으로 의존 관계를 주입한다. 여러 개 주입도 한 번에 가능하다.

 

자동 관계 주입(여기서는 생성자 주입 방법)은 스프링 빈에 등록된 것 중 타입이 같은 것을 선택하여 가져와 주입한다. 위 코드에서는 MemberRepository 클래스 타입에 해당하는 것 중 스프링 빈에 등록된 것이 있으면 가져올 것이다 (여러 개 있어서 충돌 가능한 경우는 이 글 맨 아래에 적어놓았음)

 

 

아래 코드와 같이 filter 기능으로 스프링 빈에 포함할 클래스와 배제할 클래스를 정할 수 있다.

package hello.core.scan.filter;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.springframework.context.annotation.ComponentScan.*;

public class ComponentFilterAppConfigTest {

    @Test
    void filterScan() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
        BeanA beanA = ac.getBean("beanA", BeanA.class);
        assertThat(beanA).isNotNull();

        assertThrows(
                NoSuchBeanDefinitionException.class,
                () -> ac.getBean("beanB", BeanB.class));
    }

    @Configuration
    @ComponentScan(
            includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
            excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
    )
    static class ComponentFilterAppConfig {

    }
}

 

하지만 정작 필터 기능을 많이 사용하지는 않는다고 한다. 스프링의 기본 설정에 맞추어 사용하는 것을 권장.

 

 

*컴포넌트 스캔 중 스프링 빈이 중복되어 충돌하는 경우

-만약 중복되는 빈 두개가 전부 자동 등록, 즉 @Component를 통해 생성되었다면 오류 메시지를 일으킨다.

-하지만 수동 등록(@Bean)과 자동 등록이 중복되면 수동 등록한 것이 우선되고 오버라이딩 되어 등록된다

--> 개발자가 의도하지 않은 경우가 대부분이고 Bean이 많을 수록 알 수 없는 에러가 발생할 수 있다

--> 따라서 최근 스프링부트는 이러한 경우를 처음부터 예방하기 위해 오류 메시지를 일으키도록 설정되어있다.

 

참고: 김영한 - 스프링 핵심 기본 원리(인프런)

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/03   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함