(스프링 핵심원리) 컴포넌트 스캔과 의존관계 자동 주입
스프링 핵심 원리 - 기본편 (김영한)
컴포넌트 스캔
컴포넌트 스캔이란?
- 스프링은 스프링 빈을 수동으로 설정 정보를 통해 등록할 수 도 있지만, 설정 정보 없이 자동으로 스프링 빈을 등록하는
컴포넌트 스캔
이라는 기능을 제공한다. - 컴포넌트 스캔은
@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
: 스프링 설정 정보에서 사용, 스프링 빈이 싱글톤이 유지하도록 추가 처리한다.
중복 등록과 충돌
- 같은 이름의 자동 빈이 여러개 등록된 상황
- 스프링은
ConfilictingBeanDefinitionException
예외를 발생시킨다.
- 스프링은
- 같은 이름의 자동 빈과 수동 빈이 등록된 상황
- 스프링은 수동 빈에 우선권을 주어 수동 빈만을 등록한다.
- 수동 빈이 자동 빈을 오버라이딩 하여 자동 빈은 등록되지 않는다.
의존관계 자동 주입
의존관계 주입은 다음과 같이 크게 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설정 정보에 빈을 추가해야하는 번거로움이 있다.
- 빈의 개수가 많아질 수록 설정 정보의 크기가 커져 부담이 커진다.
- 따라서, 수동 빈에 비해 자동 빈을 더 많이 사용한다.
수동 빈을 사용하는 경우
- 기술지원 로직은 업무 로직에 비해 수가 적고, 문제 발생 시 문제 파악이 어렵다.
- 따라서, 수동 빈 등록을 통해 명확하게 드러내는게 더 좋다.