[Spring] 6-1. 컴포넌트 스캔

송광호·2024년 1월 3일

[Spring]

목록 보기
24/41
post-thumbnail

Spring 시리즈는 혼자 공부하며 기록으로 남기고, 만약 잘못 학습 한 지식이 있다면 공유하며 피드백을 받고자 작성합니다.
스프링에 대해 깊게 공부해보고자 인프런의 김영한 강사님께서 강의를 진행하시는 (스프링 핵심 원리 - 기본편) 강의를 수강하며 정리하는 글입니다.
혹여나 글을 읽으시며 잘못 설명된 부분이 있다면 지적 부탁드리겠습니다.


컴포넌트 스캔과 의존관계 자동 주입

  • 지금까지 한 과정을 생각해보면 전부 @Bean이나 xml의 <bean> 등을 통해서 설정 정보에 직접 등록할 스프링 빈들을 작성하였다.
  • 만약 등록해야할 스프링 빈이 수십 수백개라면?
    • 일일이 하나하나 등록하기도 귀찮다.
    • 설정 정보의 규모가 커진다.
    • 개발자가 누락시키는 문제가 발생한다.
  • 위와같은 이유로 인해 스프링은 설정정보 없이 필요한 정보를 스캔하여 스프링 빈으로 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
  • 옛날에 프로젝트 할때 썼던건데 의존관계를 자동 주입해주는 @Autowired 기능도 제공한다.
    • 근데 이거 쓰는게 좋은 방식은 아니라고 들었던거같은데.. 그래서 생성자 주입으로 변경했던 기억이 있다.
    • 왜 그런지 이유는 찾아보지 않았는데 이후 강의에서 나오지 않으면 추후에 정리 해볼 예정

기존의 설정정보는 유지하기 위해 AutoAppConfig.java 를 새로 만들 것이다

AutoAppConfig.java

@Configuration
@ComponentScan(
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {

}
  • 컴포넌트 스캔을 사용하기 위해서는 먼저 @ComponentScan애노테이션을 설정 정보에 붙여줘야한다.

참고: 원래는 @ComponentScan만 붙여주면 되지만 @Configuration 또한 컴포넌트 스캔의 대상이 되므로 현재 코드에서는 제외시켜주기위해 필터옵션을 추가했다.
보통 설정 정보를 컴포넌트 스캔 대상에서 제외하지 않지만, 기존 예제 코드를 최대한 남기고 유지하기 위해서 사용한다.

  • 컴포넌트 스캔은 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록하는 방식이다.
  • @Configuration 애노테이션을 타고 들어가보면 @Component 애노테이션이 붙어있는걸 볼 수 있다.
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {}

@Component 애노테이션 추가

  • 각 클래스가 컴포넌트 스캔의 대상이 되도록 @Component 애노테이션을 추가해보자

MemoryMemberRepository

@Component
public class MemoryMemberRepository implements MemberRepository {}

RateDiscountPolicy

@Component
public class RateDiscountPolicy implements DiscountPolicy {}

MemberServiceImpl

@Component
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    @Autowired // ac.getBean(MemberRepository.class) - 근데 만약 MemberRepository 타입이 두개면 어떻게되는걸까..
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}
  • 이전에는 의존관계를 설정 정보에서 작성하였는데 이제는 자동주입이기 때문에 의존관계 주입도 이 클래스에서 해결해야한다.
  • @Autowired는 의존관계를 자동으로 주입해준다고 한다. 주석처리 되어있는것처럼 동작하는것같은데.. 만약 MemberRepository 타입이 여러개가 등록이 되어있다면? 어떻게되는걸까.. 라는 의문이 생긴다.(이것도 설명 해주시겠지..?)

OrderServiceImpl

@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;
    }
}
  • OrderServiceImpl로 마찬자기로 추가해준다. @Autowired를 사용하면 생성자에서 여러개의 의존관계 주입도 한번에 가능하다.

테스트

public class AutoAppConfigTest {
    @Test
    void basicScan() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);
    }
}
  • 한번 실행시키고 로그를 확인해보자
ClassPathBeanDefinitionScanner -- RateDiscountPolicy.class
ClassPathBeanDefinitionScanner -- MemberServiceImpl.class
ClassPathBeanDefinitionScanner -- MemoryMemberRepository.class
ClassPathBeanDefinitionScanner -- OrderServiceImpl.class

로그를 보면 잘 스캔되어 등록되는걸 볼 수 있다.

  • 대충 이해는 가는데.. 뭔가 등록이 어떻게 되는지 그림이 잘 그려지지 않는다.

동작 구조 그림

  1. @ComponentScan
  • @ComponentScan@Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
  • 빈의 작명 방식은 클래스이름을 그대로 사용하지만 맨 앞글자만 소문자로 변경한다.
    • 빈의 이름을 직접 작명하고 싶으면 @Component("name") 으로 파라미터를 부여하면 된다.
  1. @Autowired 의존관계 자동 주입
  • 빈으로 등록을 완료했다면 의존관계를 주입할 차례다.
  • 위에서 @Autowired를 사용할때 어떻게 동작하는지 몰랐는데 먼저 컴포넌트를 스캔한 다음 필요한 의존관계를 스프링 컨테이너 안에서 가져와 주입해주는 방식인가보다.
  • 의존관계가 여러개인 경우에도 동일하다.

0개의 댓글