[add] 컴포넌트 스캔과 의존관계 자동 주입 시작하기 [#28] #29
지금까지 스프링 빈을 등록할 때는 자바 코드의 @Bean이나 XML의 등을 통해서
설정 정보에 직접 등록할 스프링 빈을 나열했다.
예제에서는 몇개가 안되었지만, 이렇게 등록해야 할 스프링 빈이 수십, 수백개가 되면
일일이 등록하기도 귀찮고, 설정 정보도 커지고, 누락하는 문제도 발생한다.
그래서 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는
컴포넌트 스캔이라는 기능을 제공한다.
또 의존관계도 자동으로 주입하는 @Autowired 라는 기능도 제공한다.
코드로 컴포넌트 스캔과 의존관계 자동 주입을 알아본다.
기존 AppConfig.java는 과거 코드와 테스트를 유지하기 위해 남겨두고,
새로운 AutoAppConfig.java를 만들었다.
AutoAppConfig.java
컴포넌트 스캔을 사용하려면 먼저 @ComponentScan 을 설정 정보에 붙여주면 된다.
기존의 AppConfig와는 다르게 @Bean으로 등록한 클래스가 하나도 없다
@ComponentScan
컴포넌트 스캔은 이름 그대로 @Component 애노테이션이 붙은 클래스를 스캔해서
스프링 빈으로 등록한다
@ComponentScan(excludeFilters = @ComponentScan.Filter (type= FilterType.ANNOTATION, classes = Configuration.class))
스프링 bean으로 자동 등록하는데 그 중에서 뺄 것을 지정하는 것이다.
여기서는 Configuration.class를 뺄 것이다. → Configuration 어노테이션이 붙은 것은 뺄 것이다.
(필터 타입은 annotation)
Configuration을 제외 한 이유?
컴포넌트 스캔을 사용하면 @Configuration 이 붙은 설정 정보도 자동으로 등록되기 때문에,
AppConfig, TestConfig 등 앞서 만들어두었던 설정 정보도 함께 등록되고, 실행되어 버린다.
그래서 excludeFilters 를 이용해서 설정정보는 컴포넌트 스캔 대상에서 제외했다.
보통 설정 정보를 컴포넌트 스캔 대상에서 제외하지는 않지만,
기존 예제 코드를 최대한 남기고 유지하기 위해서 이 방법을 선택했다
AppConfig가 자동으로 등록이 되면 안된다.
수동으로 등록하는 건데 안그러면 충돌이 날 것이다.
@Configuration을 클릭하면 @Component가 붙어있는 것을 볼 수 있다.
* @Configuration 이 컴포넌트 스캔의 대상이 된 이유도 @Configuration 소스코드를 열어보면
@Component 어노테이션이 붙어있기 때문이다.
* shift + shift : 검색
❓ : 그런데 AutoAppConfig에 @ComponentScan에서 @Configuration부분을 대상에서 제외했는데
왜 AutoAppConfig 코드에 @Configuration을 넣은건지 궁금해졌다.
@Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter (type= FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {
}
🅰️ : 테스트에서는 AutoAppConfig.class를 ApplicationContext를 생성할 때
파라미터로 넘겨줘서 빈으로 등록된다.
그러나 테스트가 아니라 메인에서 애플리케이션을 실행시킨다면 얘기가 달라진다.
@Configuration이 붙어있어야 빈으로 등록된다.
@Configuration을 붙이기 싫다면 메인 메서드 내에서도 해당 configuration 클래스를
ApplicationContext로 넘겨주면 된다.
출처: https://www.inflearn.com/questions/455061
❓: 그렇다면 AutoAppConfig에서 excludeFilters로 제외를 해도
AutoAppConfig 클래스의 @Configuration 부분은 적용이 안되는건지 궁금해졌다.
🅰️ : AutoAppConfig는 다음과 같이 직접 등록한 부분이기 때문에 포함된다.
컴포넌트 스캔과 무관하게 포함된다.
new AnnotationConfigApplicationContext(AutoAppConfig.class)
출처 : https://www.inflearn.com/questions/462112
❓ 그런데 @Component 어노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다면
@Configuration이 필요없는게 아닐까?
🅰️
@Configuration을 붙인 이유는 @Bean이 붙은 메서드를 통해 객체를 생성하려고 할 때
(내부 의존관계를 위해 직접 메서드 호출을 하게 되면) proxy를 통해 내부적으로 빈 목록을 먼저 찾아서
이미 생성된 빈이 있다면 이미 생성된 빈을 반환하고자 함이다.
ComponentScan을 하든
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class) 를 하든
빈의 등록 방식은 싱글톤이지만
Configuration 어노테이션을 사용해야지만 중복된 객체를 생성하지 않고 등록한 빈을 주입해준다
출처: https://www.inflearn.com/questions/394925
💡
@Configuration은 단순히 설정 클래스임을 명시적으로 나타내는 것 이외에도
@Component와 조금 다르게 동작한다.
먼저, @Component, @Configuration 내 @Bean 메서드는 최초 호출시 그 리턴 객체가 빈으로 등록된다.
이후 다시 @Bean 메서드를 호출하게 되면 @Configuration의 경우 프록시 객체가 호출되며
스프링 컨테이너로부터 빈을 찾아서 최초에 등록된 빈을 반환해주므로 싱글톤이 보장된다.
그러나 @Component의 경우 스프링 컨테이너로부터 빈을 찾아 반환해주지 않고
new()를 통해 새롭게 만들어진 객체를 반환해준다.
이는 큰 차이이며 @Component 내 @Bean 메서드를 사용하게 될 때는 이 차이를 알고 사용해야 한다.
+)
강의 내에서 @Configuration과 @ComponentScan을 함께 사용하게 된 것은 excludeFilter 때문이다.
일반적인 상황에서는 @SpringBootApplication 내 @ComponentScan이 존재하므로
따로 @ComponentScan을 사용하진 않는다.
물론 수동 빈 등록을 위해 @Configuration을 사용하여 설정 클래스를 별도로 만들어주는 경우는 많다.
출처 : https://www.inflearn.com/questions/348474
처음에는 😵💫 이런 느낌이었는데 위 글을 보고 ❗ 느낌을 받았다.
@Component를 붙여준다.
MemoryMemberRepository
RateDiscountPolicy
MemberServiceImpl
의존관계 주입
AppConfig에서는
memberService는 memberRepository 의존관계 주입할 것이라고 명시를 할 수 있었다.
// AppConfig
@Bean // 스프링 컨테이너에 등록
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
그런데 AutoAppConfig에는 아무것도 적혀있지 않다.
그냥 MemberServiceImpl이 스프링 빈으로 등록이 되어버린다.
의존관계 주입을 해줄 수 있는 방법이 없다.
그래서 자동의존관계 주입이 필요하다. → @Autowired
@Autowired를 생성자에 붙여주면 스프링이 memberRepository 타입에 맞는 것을 찾아와서
의존관계 주입을 자동으로 연결해서 주입해준다.
그래서 Component를 쓰면 Autowired를 쓰게 된다.
의존관계를 설정할 수 있는 방법이 없으므로
마치 ac.getBean(MemberRepository.class)와 같이 동작을 한다라고 이해하면 된다.
정리
이전에 AppConfig에서는 @Bean 으로 직접 설정 정보를 작성했고, 의존관계도 직접 명시했다.
이제는 이런 설정 정보 자체가 없기 때문에, 의존관계 주입도 이 클래스 안에서 해결해야 한다.
@Autowired 는 의존관계를 자동으로 주입해준다
OrderServiceImpl
@Autowired 를 사용하면 생성자에서 여러 의존관계도 한번에 주입받을 수 있다.
Test 코드 작성하기
@Test
void basicScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
assertThat(memberService).isInstanceOf(MemberService.class);
}
AnnotationConfigApplicationContext 를 사용하는 것은 기존과 동일하다.
설정 정보로 AutoAppConfig 클래스를 넘겨준다.
실행해보면 기존과 같이 잘 동작하는 것을 확인할 수 있다.
로그를 잘 보면 컴포넌트 스캔이 잘 동작하는 것을 확인할 수 있다.
참고로 아래 코드에서
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
AnnotationConfigApplicationContext ac 부분을 ApplicationContext ac 으로 변경해도 된다.
참고 >> https://www.inflearn.com/questions/47449
그림으로 보기
@ComponentScan
@ComponentScan 은 @Component 가 붙은 모든 클래스를 스프링 빈으로 등록한다.
이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
빈 이름 기본 전략: MemberServiceImpl 클래스 → memberServiceImpl
빈 이름 직접 지정: 만약 스프링 빈의 이름을 직접 지정하고 싶으면
@Component("memberService2") 이런식으로 이름을 부여하면 된다.
단, 구현체에 해줘야 한다. ex) MemberServiceImpl
@Autowired 의존관계 자동 주입
생성자에 @Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
이때 기본 조회 전략은 타입이 같은 빈을 찾아서 주입한다.
memberRepository와 memoryMemberRepository는 타입이 맞다.
(자식 타입이기 때문에 타입이 맞다.)
getBean(MemberRepository.class) 와 동일하다고 이해하면 된다.
* 생성자에 파라미터가 많아도 모두 찾아서 자동으로 주입해준다. (pdf 참고)
'TIL' 카테고리의 다른 글
198일차(모험 107일차) - 복습 (0) | 2022.03.31 |
---|---|
197일차(모험 106일차) - 알고리즘 (0) | 2022.03.30 |
195일차(모험 104일차) - @Configuration과 바이트코드 조작의 마법 (0) | 2022.03.29 |
인스타그램 실행과정 gif (0) | 2022.03.28 |
192일차(모험 101일차) - 프로젝트 (0) | 2022.03.25 |