[스프링 핵심] 컴포넌트 스캔
미리보기
✅ 컴포넌트 스캔에 대해 알아보자!
✅ 스캔 대상이 무엇인지 알아보자!
🍰 컴포넌트 스캔 (Component Scan)
설정 정보 파일을 통해 스프링 빈으로 등록하는 것은 한계가 있다.
등록이 필요한 빈이 엄청나게 많아진다면? 일일이 그것들을 관리하는 것도 힘들 것이다!
그래서 스프링에는 설정 정보가 없어도 자동으로 스프링 빈으로 등록할 수 있는 기능이 존재한다.
바로 '컴포넌트 스캔 (Component Scan)' 이라는 개념이다!
@Component
public class ReservationServiceImpl implements ReservationService {
private final TicketReserve ticketReserve;
private final MemberRepository memberRepository;
@Autowired
public ReservationServiceImpl(TicketReserve ticketReserve, MemberRepository memberRepository) {
this.ticketReserve = ticketReserve;
this.memberRepository = memberRepository;
}
...
}
@Component
public class TicketReserveImpl implements TicketReserve{
...
}
@Component
public class JPAMemberRepository implements MemberRepository {
...
}
일단 각 구현체 클래스에 @Component 어노테이션을 붙여주면 컴포넌트 스캔의 대상이 된다.
그리고 ReservationServiceImpl 클래스에는 @Autowired 어노테이션을 통해 생성자 주입을 진행했다.
생성자에 @Autowired 어노테이션을 붙여주면 스프링 빈의 타입을 조회해서, 타입에 맞게 자동으로 빈을 연결해준다!
위 코드 상으로 현재 스프링 컨테이너에 등록된 스프링 빈의 이름과 타입을 다음 표에 정리해보겠다.
빈 이름 | 타입 |
reservationServiceImpl | ReservationService |
ticketReserveImpl | TicketReserve |
JPAMemberRepository | MemberRepository |
ReservationServiceImpl 생성자의 파라미터 중, ticketReserve 변수의 타입은 'TicketReserve' 이다.
그렇다면 스프링 컨테이너는 TicketReserve 타입인 ticketReserveImpl 빈을 찾아서 매칭해주는 것이다!
결론적으로 ReservationServiceImpl 클래스에는 ticketReserveImpl, JPAMemberRepository 빈이 자동으로 매칭된다.
실제로 스프링 컨테이너에 올려서 코드를 돌려보고, 작성된 로그를 살펴보자. 상당히 흥미롭다!
/* @Component 어노테이션 스캔 과정 */
[main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/Desktop/Study/core/out/test/classes/hello/core/JPAMemberRepository.class]
[main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/Desktop/Study/core/out/test/classes/hello/core/ReservationServiceImpl.class]
[main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [/Users/Desktop/Study/core/out/test/classes/hello/core/TicketReserveImpl.class]
/* 스프링 빈 생성 과정 */
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'JPAMemberRepository'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'reservationServiceImpl'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'ticketReserveImpl'
/* 의존관계 자동 주입 과정 */
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'reservationServiceImpl' via constructor to bean named 'ticketReserveImpl'
[main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'reservationServiceImpl' via constructor to bean named 'JPAMemberRepository'
일단 @Component 어노테이션이 붙은 클래스들을 찾기 위해, 컴포넌트 스캔 과정을 거친다.
그리고 스캔한 결과를 바탕으로 스프링 빈을 생성했다. 그런데 밑에 2개의 로그가 상당히 흥미롭다!
Autowiring by type from bean name 'reservationServiceImpl' via constructor to bean named 'ticketReserveImpl'
'reservationServiceImpl' 이라는 빈을, 생성자를 통해 'ticketReserveImpl' 이라는 빈에 Autowiring 했다는 뜻이다! ⭐️⭐️
이제는 설정 정보 파일을 따로 작성하지 않고도, 컴포넌트 스캔과 @Autowired를 통한 생성자 주입을 통해 의존관계를 구현했다!
☕️ 컴포넌트 스캔 기본 대상
컴포넌트 스캔을 진행할 때, 모든 어노테이션을 스캔하는 것이 아니고, 정해진 어노테이션들만 스캔을 진행한다.
@Component 어노테이션은 포괄적인 개념이다. 무슨 말이냐면! 아래 어노테이션들을 포함한다는 의미이다.
1. @Controller, @Service, @Repository
MVC 컨트롤러 역할을 하는 @Controller, 비즈니스 로직 구현 계층인 @Service, 데이터 접근 역할을 하는 @Repository 까지!
실제로 @Service 어노테이션을 타고 가보면, @Component 어노테이션이 붙은 것을 확인할 수 있다.
2. @Configuration
이는 설정 정보 파일을 의미하는 어노테이션도 컴포넌트 스캔 대상에 포함된다. 또한 @Configuration 어노테이션의 중요한 점!
이전 포스팅에서 공부했던 내용('@CGLIB' 가 붙은 프록시 객체를 생성해서 싱글톤 빈이 유지되도록 구현)을 잊지 않도록 하자!
참고로 어노테이션을 인식하는 것은 자바 언어가 지원하는 기능은 아니고, 스프링이 지원하는 기능이다!
결론적으로, @Component 어노테이션에는 위 4가지 어노테이션들이 포함되어 있다!
하나 더! 스프링이 작동할 때, 컴포넌트 스캔이 자동으로 진행되는 이유는 무엇일까?
스프링을 실행하려면 프로젝트 내 기본으로 만들어져있는 위 코드를 실행해야하는데,
위에 잘 보면 @SpringBootApplication 어노테이션이 붙어있다. 저기에 바로 답이 숨어 있다!
스프링을 실행하기 위한 @SpringBootApplication 안에 @ComponentScan 어노테이션이 포함되어 있다.
@ComponentScan 어노테이션은 컴포넌트 스캔을 하도록 지시하는 어노테이션이다! 이제 원리를 점차 알아가는 것 같다 😃