이 글은 강의 : 김영한님의 - "스프링 핵심원리 - 기본편"을 듣고 정리한 내용입니다. 😁😁
이번 챕터부터 컴포넌트 스캔에 대하여 공부할 것이다.
이번에는 컴포넌트 스캔과 의존관계 자동 주입에 대해서 공부해보자.
지금까지는 자바 코드의 @Bean이나 XML의 을 통해서 설정 정보에 직접 스프링 빈을 등록했다.
예제에서는 등록해야할 스프링 빈이 몇 개가 안됐지만 규모가 커지면 반복과 누락의 문제가 있다... 그래서
-스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
- 의존관계도 자동으로 주입하는 @Autowired
라는 기능도 제공한다.
기존 AppConfig.java는 과거 코드, 테스트를 유지하기 위해 남겨두고 새로운 AutoAppConfig.java를 만들자.
컴포넌트 스캔을 사용하려면 @ComponentScan을 설정 정보에 붙여준다.
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
// 설정 정보
@Configuration
// @Component가 붙은 클래스를 자동으로 스프링 빈으로 등록(@Configuration이 붙은 클래스는 제외)
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
(참고) 컴포넌트 스캔을 사용하면 @Configuration이 붙은 설정 정보가 자동으로 스프링 빈으로 등록된다.
이전에 만들었던 AppConfig, TestConfig 등의 설정 정보를 스프링 빈으로 등록되면 안되기 때문에 excludeFilters 를 사용하여 컴포넌트 스캔 대상에서 제외했다. (보통 설정 정보를 컴포넌트 스캔 대상에서 제외하지는 않음)
@Configuration이 붙은 클래스를 컴포넌트 대상에서 제외했지만!! (@Configuration이 붙은)AutoConfig는 직접 등록했기 때문에 컴포넌트 스캔과 무관하게 스프링 빈으로 등록된다.
(new Anno---로 등록했잖아!)
각 클래스가 컴포넌트 스캔의 대상으로서 자동으로 스프링 빈 등록되도록 @Component 어노테이션을 붙인다.
즉! MemoryMemberRepository @Component 추가, RateDiscountPolicy @Component 추가
MemoryMemberRepository, RateDiscountPolicy가 자동으로 스프링 빈으로 등록된다.
@Component
public class MemoryMemberRepository implements MemberRepository{
...
}
@Component
public class RateDiscountPolicy implements DiscountPolicy{
...
}
MemberServiceImpl @Component, @Autowired 추가
@Component
public class MemberServiceImpl implements MemberService{
// MemberServiceImpl 은 MemberRepository 인터페이스에만 의존
private final MemberRepository memberRepository;
// 의존관계 자동 주입(DI)
@Autowired // ac.getBean(MemberRepository.class)
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
MemberServiceImpl은 자동으로 스프링 빈으로 등록된다. MemberServiceImpl의 생성자의 파라미터로 MemberRepository 타입의 빈이 자동으로 주입된다.
OrderServiceImpl @Component, @Autowired 추가
@Component
public class OrderServiceImpl implements OrderService{
// 인터페이스에만 의존하도록 변경(DIP 만족)
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// 의존관계 자동 주입(DI)
@Autowired // ac.getBean(MemberRepository.class), ac.getBean(DiscountPolicy.class)
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
OrderServiceImpl은 자동으로 스프링 빈으로 등록된다.
OrderServiceImpl의 생성자의 파라미터로 MemberRepository, DiscountPolicy 타입의 빈이 자동 주입된다.
AnnotationConfigApplicationContext()의 설정정보를 AutoAppConfig 클래스로 넘겨준다. (나머지 부분은 동일)
package hello.core.scan;
import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.assertj.core.api.Assertions.*;
public class AutoAppConfigTest {
@Test
void basicScan() {
// 설정 정보를 AutoAppConfig로 넘겨서 스프링 컨테이너 생성
// AutoConfig는 스프링 빈으로 직접 등록됨
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
// MemberService 조회
MemberService memberService = ac.getBean(MemberService.class);
// memberService 인스턴스가 MemberService 타입인지 검증
assertThat(memberService).isInstanceOf(MemberService.class);
}
}
결과보면 컴포넌트 스캔이 잘 동작해 스프링 빈이 자동으로 등록된 것을 확인할 수 있다.
1) @ComponentScan
@ComponentScan은 @Component가 붙은 모든 클래스를 자동으로 스프링 빈으로 등록한다.
@Configuration이 붙은 클래스도 스프링 빈으로 자동 등록된다. (@Configuration 내부에 @Component 가 있음)
<스프링 빈 이름>
1) 빈 이름 기본 지정: 클래스 명(맨 앞글자만 소문자로 변경)
e.g. MemberServiceImpl 클래스 -> memberServiceImpl
2) 빈 이름 직접 지정: @Component(스프링 빈 이름)
e.g. @Component("memberService2") -> memberService2
2) @Autowired 의존관계 자동 주입
생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
기본 조회 전략은 타입이 같은 빈을 찾아서 주입하는 것이다.
생성자에 파라미터가 많아도 여러 의존관계를 한번에 주입받을 수 있다.