Spring

[스프링 핵심 원리 - 기본편] 컴포넌트 스캔 1

경딩 2024. 10. 18. 23:27

컴포넌트 스캔과 의존관계 자동 주입 시작하기

  • 지금까지 스프링 빈을 등록할 때는 자바 코드의 @Bean이나 <bean>을 통해 설정 정보를 직접 등록할 스프링 빈을 나열했다.
  • 예제는 등록해야 할 스프링 빈이 몇 개 안 되었지만 실무에서 수천, 수만 개가 된다면 설정정보도 커지고 개발자가 빈 등록을 누락할 수 도 있다.
  • 이를 해결하기 위해 스프링에서 설정정보 (AppConfig.class)가 없어도 자동으로 스프링 빈을 제공하는 컴포넌트 스캔이라는 기능을 제공하다.
  • 또 의존관계도 자동으로 주입하는 @Autowired라는 기능도 제한한다.

코드로 알아보기!

package hello.core;

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

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

먼저 기존 AppConfig.java는 과거 코드와 테스트를 유지하기 위해 남겨두고, 새로운 AutoAppConfig.java를 만들자.

 

참고 : excludeFilters을 하는 이유

컴포넌트 스캔을 사용하면 @Configuration 이 붙은 설정 정보도 자동으로 등록되기 때문에, AppConfig에 만들어두었던 설정정보도 함께 등록되고 실행되어 버린다. 수동으로 등록된 빈과 Component로 스캔되어 등록된 빈들이 충돌할 수 있다.

그래서 excludeFilters을 이용해서 설정정보는 컴포넌트 스캔 대상에서 제외했다. 보통 설정 정보를 컴포넌트 스캔 대상에서 제외하지 않지만, 기존 예제 코드를 유지하기 위해 이 방법을 선택했다.

 

 

컴포넌트 스캔을 사용하기 위해서는 @ComponentScan@ComponentScan을 설정정보에 붙여주어야 한다.

기존에 AppConfig 와는 달린 @Bean으로 등록한 클래스가 하나도 없는 것을 확인할 수 있다.

 

컴포넌트 스캔은 이름 그대로 @Componenet 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.

@configuration 이 컴포넌트 스캔대상이 된 이유는 @Configration 소스 코드를 열어보면 @Component 가 붙어있는 것을 볼 수 있다.

 

MemoryMemberRepository @Component 추가

@Component
public class MemoryMemberRepository implements MemberRepository {}

 


RateDiscountPolicy @Component 추가 

@Component
public class RateDiscountPolicy implements DiscountPolicy {}

 

MemberServiceImpl @Component, @Autowired 추가

@Component
public class MembrServiceImpl implements MemberService{
    private final MemberRepository memberRepository;

    @Autowired
    public MembrServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
 }

 

AppConfig에서는 @Bean으로 직접 설정정보를 작성했고, 의존관계도 직접 명시하였다.

AppAutoCongfig는 이런 정보자체가 없기 때문에, 의존관계 주입도 이 클래스 안에서 해결해야 한다.

 

자동적으로 컴포넌트를 스캔해서 빈을 등록하는 컴포넌트 스캔에서 의존관계 주입을 어떻게 해줄까?

@Autowired는 의존관계를  자동으로 주입해 준다.

 

OrderServiceImpl @Component, @Autowired 추가

@Component
public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
   private  DiscountPolicy discountPolicy;

   @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
  }

@Autowired를 사용하면 생성자에서 여러 의존관계를 한 번에 주입받을 수 있다.

 

AutoAppConfigTest.java

package hello.core.scan;

import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AutoAppConfigTest {

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

        MemberService memberService = ac.getBean(MemberService.class);
        Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
    }
}
  • AnnotationConfigApplicationContext를 사용하는 것은 기존과  동일하다.
  • 설정정보로  AutoAppConfig 클래스를 넘겨준다.
  • 살행 결과 테스트가 잘 되는 것을 확인할 수 있다.

 

컴포넌트 스캔과 자동 의존관계 주입은 어떻게 동작할까?

 

1.@ComponentScan

  • @ComponentScan 은 @Component 가 붙은 모든 클래스를 스프링 빈으로 등록한다.
  • 이때 스프링 빈의 기본이름은 클래스명으로 사용하며 맨 앞글자만 소문자로 사용한다.
    • 빈 이름 기본 전략: MemberServiceImpl 클래스 -> memberServiceImpl 
    • 빈 이름을 직접 지정하고 싶으면 @Component("memberService2") `이런 식으로 이름을 부여하면 된다.


2.@Autowired 의존관계 자동주입

  • 생성자에 @Autowired를 지정하면, 스프링컨테이너가 자동으로 해당 스프링빈을 찾아서 주입한다.
  • 이때 기본 조회 전략은 타입이 같은 빈을 찾아서 주입한다.
    • getBean(MemberRepository.class)와 동일하다고 이해하면 된다.

  • 생성자에 파라미터가 많아도 다 자동으로 주입된다.