지금까지는 스프링 빈을 등록할 때 @Bean을 통해 설정 파일에 직접 등록해주었다. 그런데 등록할 빈이 수십, 수백 개가 되면 이 모든 빈을 직접 나열해가며 등록해야 할까? 스프링에서는 이 문제를 해결해주는 컴포넌트 스캔 기능을 제공한다.
컴포넌트 스캔
스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공하며, 의존관계를 자동으로 주입하는 @Autowired 기능도 제공한다.
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
// 수동으로 등록하는 AppConfig, TestConfig 등의 클래스 제외
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
컴포넌트 스캔을 사용하기 위해서는 먼저 설정 정보 클래스에 @ComponentScan 애노테이션을 붙여주면 된다. 기존과는 다르게 클래스 내부에 @Bean으로 클래스를 등록하지 않았다. 컴포넌트 스캔은 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록해준다.
package hello.core.member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired // getBean(MemberRepository.class)와 같음
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
위 코드처럼 클래스에 @Component 애노테이션을 붙이면 컴포넌트 스캔의 대상이 된다. 그런데 이전까지는 객체 간의 의존관계를 직접 명시했지만 현재는 설정 정보 자체가 없기 때문에 의존관계 주입도 이 클래스 안에서 해결해야 한다. 이때 의존관계를 자동으로 주입해주는 것이 1번째 줄의 @Autowired 애노테이션이다.
컴포넌트 스캔과 자동 의존관계 주입
컴포넌트 스캔과 자동 의존관계 주입이 어떻게 동작하는지 알아보자.
- 우선 @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다. 이때 빈의 기본 이름은 클래스 명의 맨 앞글자만 소문자로 변경하여 사용한다.
- 생성자에 @Autowired를 지정하면 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다. 이때 기본적으로 타입이 같은 빈을 찾아서 주입하므로 getBean(MemberRepository.class)와 동일하다고 생각하면 된다.
컴포넌트 스캔 탐색 위치와 기본 스캔 대상
모든 자바 클래스를 다 스캔하려면 시간이 오래 걸리기 때문에 꼭 필요한 위치부터 탐색을 시작하도록 시작 위치를 지정할 수 있다.
@ComponentScan{
basePackages = "hello.core"
}
basePackages 옵션은 탐색할 패키지의 시작 위치를 지정한다. 이 패키지를 포함해서 하위 패키지를 모두 탐색한다. basePackages = {"hello.core", "hello.service"}와 같이 여러 시작 위치를 지정하는 것도 가능하다.
그런데 시작 위치를 지정하지 않으면 설정 파일이 존재하는 패키지가 시작 위치가 되기 때문에, 설정 정보 클래스를 프로젝트의 최상단에 두어 시작 위치를 별도로 지정하지 않고 사용하는 방법을 추천한다. 스프링 부트에서는 @ComponentScan을 가지고 있는 @SpringBootApplication가 스프링 부트의 대표 시작 정보인데, 이를 프로젝트 시작 루트 위치에 두는 것이 관례이다.
컴포넌트 스캔은 @Component 뿐만 아니라 @Controller, @Service, @Repository, @Configuration도 스캔 대상에 포함한다. 이들은 스캔 용도뿐만 아니라 스프링의 부가 기능을 수행하는 역할도 한다.
중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름이 등록되는 경우가 발생할 수 있다. 자동으로 등록된 빈의 이름이 충돌하는 경우 스프링은 ConflictBeanDefinitionException을 발생시킨다. 그러나 자동으로 등록된 빈과 수동으로 등록한 빈의 이름이 같은 경우에는 수동 등록 빈이 자동 등록 빈을 오버라이딩 해버린다. 최근 스프링 부트에서는 이 경우에도 오류를 발생하도록 기본 값이 변경되었다.
이 포스트는 인프런의 [스프링 핵심 원리 - 기본편] (김영한) 강의를 수강하고 정리한 글입니다.
'Java > [스프링 핵심 원리]' 카테고리의 다른 글
[스프링] AOP 적용을 위해 @Aspect를 사용해보자 (0) | 2024.03.14 |
---|---|
[스프링] 관점 지향 프로그래밍 AOP란 무엇일까 (0) | 2024.03.14 |
[스프링 핵심 원리] 싱글톤 컨테이너와 @Configuration (0) | 2021.12.11 |
[스프링 핵심 원리] 스프링 설정 파일 알아보기 - BeanFactory와 ApplicationContext, BeanDefinition (0) | 2021.12.08 |
[스프링 핵심 원리] 스프링 컨테이너와 스프링 빈 (0) | 2021.12.07 |
댓글