컴포넌트 스캔

컴포넌트 스캔이란?

  • 스프링은 스프링 빈을 수동으로 설정 정보를 통해 등록할 수 도 있지만, 설정 정보 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
  • 컴포넌트 스캔은 @Component애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다. @Configuration, @Repository, ‘@Controller’ 등 @Component 애노테이션을 포함한 애노테이션도 컴포넌트 스캔의 대상이 된다.

컴포넌트 스캔 과정

1. @ComponentScan

package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.springframework.context.annotation.ComponentScan.*;
@Configuration
@ComponentScan(
 excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
Configuration.class))
public class AutoAppConfig {
}
  • 컴포넌트 스캔을 사용하기 위해선 @ComponentScan애노테이션을 설정 정보에 붙이면 된다.
  • @ComponentScan@Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.

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

  • @Component가 붙은 클래스의 생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 의존관계를 주입한다.
  • 생성자의 파라미터가 여러개이더라도 모두 찾아 자동으로 주입한다.

컴포넌트 스캔 기본 대상

  • 컴포넌트 스캔은 @Component뿐만 아니라 @Component를 포함한 모든 애노테이션을 컴포넌트 스캔 대상으로 한다.
    • @Component : 컴포넌트 스캔에서 사용
    • @Controller : 스프링 MVC 컨트롤러에서 사용
    • @Service : 스프링 비즈니스 로직에서 사용
    • @Repository : 스프링 데이터 접근 계층에서 사용, 데이터 계층 예외를 스프링 계층 예외로 변환해준다.
    • @Configuration : 스프링 설정 정보에서 사용, 스프링 빈이 싱글톤이 유지하도록 추가 처리한다.

중복 등록과 충돌

  1. 같은 이름의 자동 빈이 여러개 등록된 상황
    • 스프링은 ConfilictingBeanDefinitionException예외를 발생시킨다.
  2. 같은 이름의 자동 빈과 수동 빈이 등록된 상황
    • 스프링은 수동 빈에 우선권을 주어 수동 빈만을 등록한다.
    • 수동 빈이 자동 빈을 오버라이딩 하여 자동 빈은 등록되지 않는다.

의존관계 자동 주입

의존관계 주입은 다음과 같이 크게 4가지 방법으로 할 수 있다.

  • 생성자 주입
  • 수정자 주입
  • 필드 주입
  • 일반 메서드 주입

생성자 주입

@Component
public class ServiceImpl implements Service {
   private final Repository repository;

   @Autowired
   public ServiceImpl(Repository repository) {
     this.repository = repository;
   }
}
  • 생성자를 통해서 의존 관계를 주입받는다.
  • 대부분의 의존관계 자동 주입은 생성자 주입을 통해서 이루어진다.
  • 클래스가 생성될 때 딱 한번만 호출된다. 따라서, 애플리케이션 종료시점까지 의존 관계의 변경이 없다.
  • @Autowired를 통해 자동으로 의존 관계가 주입된다.
  • 불변, 필수 의존관계에 사용한다.
  • final키워드를 사용하여 생성자에서 값이 설정되지 않는 오류를 컴파일 시점에 막도록 할 수 있다.

수정자 주입 (setter 주입)

@Component
public class ServiceImpl implements Service {
   private final Repository repository;

   @Autowired
   public void setRepository(Repository repository) {
     this.repository = repository;
   }
}
  • setter 필드의 값을 변경하는 수정자 메서드를 통해 의존관계를 주입받는다.
  • 선택, 변경 가능성이 있는 의존관계에 사용한다.

필드 주입과 일반 메서드 주입

  • 필드 주입은 필드에 의존관계를 바로 주입하는 것을 말한다.
  • 일반 메서드 주입은 일반 메서드를 통해서 의존관계를 주입 받는다.
  • 두 주입 방식 모두 일반적으로 잘 사용하지 않는다.

롬복

  • 롬복은 생성자 주입 코드의 반복을 없애 코드 작성을 편리하게 해준다.
  • 롬복 사용 X
    @Component
    public class ServiceImpl implements Service {
       private final Repository repository;
       //코드가 반복적으로 사용된다.
       @Autowired
       public ServiceImpl(Repository repository) {
     this.repository = repository;
       }
    }
    
  • 기존 생성자 주입 코드는 생성자 주입을 사용하는 코드에 모두 반복적으로 들어간다.

롬복 사용

@Component
@RequiredArgsConstructor
public class ServiceImpl implements Service {
   private final Repository repository;
}
  • @RequiredArgsConstructor기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어준다.
  • 이렇게 롬복을 사용하면 매우 간결하게 코드를 작성할 수 있다.

@Autowired 필드 명, @Qualifier, @Primary

  • 타입으로 조회되는 빈이 2개 이상일 때, 스프링 오류가 발생한다.
  • @Autowired 필드 명, @Qualifier, @Primary으로 해결할 수 있다.

@Autowired 필드 명 매칭

@Autowired
private DiscountPolicy rateDiscountPolicy
  • 필드 명을 빈 이름으로 변경한다.
  • 필드 명 매칭은 먼저 타입 매칭을 시도하고, 그 결과에 여러 빈이 있을 때 추가로 동작한다.

@Qualifier

@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
   @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
   this.memberRepository = memberRepository;
   this.discountPolicy = discountPolicy;
}
  • @Qualifier애노테이션을 사용하여 직접적으로 매칭될 빈 이름을 매개변수로 넣어준다.

@Primary

@Component
@Primary
  public class RateDiscountPolicy implements DiscountPolicy {}
@Component
  public class FixDiscountPolicy implements DiscountPolicy {}
  • 위처럼 두개의 조회 빈이 발생할 때 @Primary로 등록한 빈에 우선권이 존재하여 해당 빈만 조회된다.
  • 모든 코드에 @Qualifier를 붙여줘야했던 것과는 달리 주입할 클래스에만 @Primary를 붙이면 되기 때문에 매우 편리하다.
  • @Qualifier이 더 상세하게 동작하기 때문에 @Primary보다 우선순위가 높다.

자동, 수동의 올바른 실무 운영 기준

  • 수동 빈을 사용했을 때, 스프링 빈을 추가할 때마다 @Configuration설정 정보에 빈을 추가해야하는 번거로움이 있다.
  • 빈의 개수가 많아질 수록 설정 정보의 크기가 커져 부담이 커진다.
  • 따라서, 수동 빈에 비해 자동 빈을 더 많이 사용한다.

    수동 빈을 사용하는 경우

  • 기술지원 로직은 업무 로직에 비해 수가 적고, 문제 발생 시 문제 파악이 어렵다.
  • 따라서, 수동 빈 등록을 통해 명확하게 드러내는게 더 좋다.