(인프런) 김영한님의 스프링 핵심 원리-기본편을 공부하고 리뷰한 글입니다.
1. 컴포넌트 스캔과 의존관계 자동 주입 시작하기
지금까지는 자바 코드의 @Bean이나 XML의 <bean>을 통해서 설정 정보에 직접 스프링 빈을 등록했다.
예제에서는 등록해야할 스프링 빈이 몇 개가 안됐지만 규모가 커지면 반복과 누락의 문제가 있다... 그래서,
1) 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.
2) 의존관계도 자동으로 주입하는 @Autowired 라는 기능도 제공한다.
기존 AppConfig.java는 과거 코드, 테스트를 유지하기 위해 남겨두고 새로운 AutoAppConfig.java를 만들자.
1. AutoAppConfig.java - @ComponentScan
1) 컴포넌트 스캔을 사용하려면 @ComponentScan을 설정 정보에 붙여준다.
2) 기존의 AppConfig와는 다르게 @Bean으로 등록한 클래스가 하나도 없다!
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 를 사용하여 컴포넌트 스캔 대상에서 제외했다. (보통 설정 정보를 컴포넌트 스캔 대상에서 제외하지는 않음)
Q. @Configuration이 붙은 클래스를 컴포넌트 대상에서 제외하면 AutoAppConfig도 제외되는 것이 아닌가?
AutoAppConfig는 @Configuration이 붙어있긴 하지만, 아래와 같이 직접 등록했기 때문에 컴포넌트 스캔과 무관하게 스프링 빈으로 등록된다.
new AnnotationConfigApplicationContext(AutoAppConfig.class)
[참고] https://www.inflearn.com/questions/462112
2. @Component, @Autowired 추가하기
각 클래스가 컴포넌트 스캔의 대상으로서 자동으로 스프링 빈 등록되도록 @Component 애노테이션을 붙인다.
1) MemoryMemberRepository @Component 추가
@Component
public class MemoryMemberRepository implements MemberRepository{
...
}
MemoryMeberRepository는 자동으로 스프링 빈으로 등록된다.
2) RateDiscountPolicy @Component 추가
@Component
public class RateDiscountPolicy implements DiscountPolicy{
...
}
RateDiscountPolicy는 자동으로 스프링 빈으로 등록된다.
3) 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 타입의 빈이 자동 주입된다.
4) 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;
}
...
}
OrderImpl은 자동으로 스프링 빈으로 등록된다.
OrderServiceImpl의 생성자의 파라미터로 MemberRepository, DiscountPolicy 타입의 빈이 자동 주입된다.
3. AutoAppConfigTest.java
AnnotationConfigApplicationContext()의 설정정보를 AutoAppConfig 클래스로 넘겨준다. (나머지 부분은 동일)
1) 코드 작성
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);
}
}
Q. new AnnotationConfigApplicationContext() 로 생성할 때 ApplicationContext 타입으로 선언한 이유는?
사용하는 코드가 ApplicationContext의 제약을 따르기 때문에 향후 변경시에 사용코드를 변경하지 않아도 된다. 구현체를 AnnotationConfigApplicationContext -> 다른 구현체 로 변경한다고 하더라도 실제 ApplicationContext 인스턴스를 사용하는 코드는 변경할 필요가 없다는 뜻이다.
클래스 외부에서 구현 객체를 생성해서 파라미터로 넘길 경우, 구현 객체가 다른 것으로 바뀌어도 다른 코드는 전혀 변경하지 않는다.
[참고] https://www.inflearn.com/questions/47449
2) 결과
로그를 보면 컴포넌트 스캔이 잘 동작해 스프링 빈이 자동으로 등록된 것을 확인할 수 있다.
4. 컴포넌트 스캔과 자동 의존 관계 주입 동작 그림
1) @ComponentScan
@ComponentScan은 @Component가 붙은 모든 클래스를 자동으로 스프링 빈으로 등록한다.
- @Configuration이 붙은 클래스도 스프링 빈으로 자동 등록된다. (@Configuration 내부에 @Component 가 있음)
<스프링 빈 이름>
1) 빈 이름 기본 지정: 클래스 명(맨 앞글자만 소문자로 변경)
e.g. MemberServiceImpl 클래스 -> memberServiceImpl
2) 빈 이름 직접 지정: @Component(스프링 빈 이름)
e.g. @Component("memberService2") -> memberService2
2) @Autowired 의존관계 자동 주입
생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
- 기본 조회 전략은 타입이 같은 빈을 찾아서 주입하는 것이다.
- 생성자에 파라미터가 많아도 여러 의존관계를 한번에 주입받을 수 있다.
'Spring > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
[스프링 핵심 원리] 06. 컴포넌트 스캔 - 중복 등록과 충돌 (0) | 2022.05.18 |
---|---|
[스프링 핵심 원리] 06. 컴포넌트 스캔 - 탐색 위치와 기본 스캔 대상 & 필터 (0) | 2022.05.18 |
[스프링 핵심 원리] 05. 싱글톤 컨테이너 - @Configuration과 싱글톤 & @Configuration과 바이트코드 조작의 마법 (0) | 2022.05.17 |
[스프링 핵심 원리] 05. 싱글톤 컨테이너 - 싱글톤 컨테이너 & 싱글톤 방식의 주의점(중요★) (0) | 2022.05.17 |
[스프링 핵심 원리] 05. 싱글톤 컨테이너 - 웹 애플리케이션과 싱글톤 & 싱글톤 패턴 (0) | 2022.05.17 |