Carefree Hub

Command Palette

Search for a command to run...

Spring 컴포넌트 스캔 (Component Scan) 이란?

Spring 컴포넌트 스캔 (Component Scan) 이란?

@ComponentScan과 @Autowired를 사용한 스프링 빈 자동 등록과 의존관계 자동 주입

Carefreelife98
3분 소요

컴포넌트 스캔과 의존 관계 자동 주입 이란?

  • 기존 스프링 빈 등록 시에 @Bean 어노테이션이나, XML과 같은 설정파일의 <bean> 태그를 사용하여 일일히 스프링 빈을 등록해주어야 했다
  • 시스템이 거대해질수록 등록해야 할 스프링 빈이 그에 비례하여 많아지고, 누락 및 오타와 같은 문제가 발생하기 쉬워지며, 유지보수에 어려움이 생긴다
  • 이에 설정 정보가 없더라도 자동으로 스프링 빈을 등록해주는 컴포넌트 스캔이라는 기능이 존재한다
  • 의존관계 역시 자동으로 주입해주는 @Autowired 어노테이션 또한 존재한다

@ComponentScan

java
1import org.springframework.context.annotation.ComponentScan;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.context.annotation.FilterType;
4
5@Configuration
6@ComponentScan(
7 excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
8)
9public class AutoAppConfig {
10
11}
  • 컴포넌트 스캔 사용 방법: @ComponentScan 어노테이션을 설정 정보 클래스 (@Configuration)에 적용
    • 컴포넌트 스캔을 적용한 설정정보 클래스 내에 @Bean을 통해 스프링 빈으로 등록한 클래스가 없는 것을 확인할 수 있다
    • 컴포넌트 스캔은 말 그대로 @Component 어노테이션이 적용된 클래스를 스캔하여 스프링 빈으로 등록한다
    • 설정 정보 클래스가 여러 개 존재하는 경우와 같이 필터를 통해 컴포넌트 스캔에서 제외할 수도 있다

@Autowired

java
1@Configuration
2public class AppConfig {
3
4 ...
5
6 @Bean
7 public MemberService memberService() {
8 return new MemberServiceImpl(memberRepository());
9 }
10
11 ...
12
13}
  • ComponentScan을 사용하기 전에는 위처럼 설정 정보 클래스에서 의존 관계 주입을 해주었다
  • 하지만 ComponentScan을 사용한 후, 설정 정보 클래스 내에서 빈을 직접 등록하지 않고, @Component 어노테이션이 적용된 클래스를 스프링 빈으로 자동 등록하기 때문에 의존 관계 주입 또한 @Autowired 메서드를 통해 해당 클래스 내에서 진행해 주어야 한다
java
1import org.springframework.beans.factory.annotation.Autowired;
2import org.springframework.stereotype.Component;
3
4@Component
5public class MemberServiceImpl implements MemberService {
6
7 private final MemberRepository memberRepository;
8
9 @Autowired
10 public MemberServiceImpl(MemberRepository memberRepository) {
11 this.memberRepository = memberRepository;
12 }
13
14 @Override
15 public void join(Member member) {
16 memberRepository.save(member);
17 }
18
19 @Override
20 public Member findMember(Long memberId) {
21 return memberRepository.findById(memberId);
22 }
23}
  • 위와 같이 구현 클래스의 생성자에 @Autowired 어노테이션을 적용하여 여러 의존관계 또한 한번에 주입받을 수 있다

컴포넌트 스캔과 자동 의존관계 주입 동작 과정

@ComponentScan

  • 컴포넌트 스캔을 사용하는 경우 자동 등록되는 스프링 빈의 기본 이름(Default)은 해당 클래스명을 사용하되, 가장 앞글자를 소문자로 변경하여 등록되며, 직접 지정 또한 가능하다
    • Default: MemberServiceImpl(클래스명) -> memberServiceImpl(스프링 빈 이름)
    • Custom: @Component("memberServiceCustom") 과 같이 직접 빈 이름 부여 가능

@Autowired

  • 생성자에 @Autowired를 적용하면, 스프링 컨테이너가 해당 스프링 빈을 찾아 자동 주입한다
  • 기본 조회 전략은 "타입 조회" 를 통한 동일 타입 빈을 조회하여 주입한다
    • getBean(MemberRepository.class)와 동일한 기능을 수행

컴포넌트 스캔의 탐색 위치 및 기본 스캔 대상

탐색 대상 패키지의 루트 경로 지정

java
1@Configuration
2@ComponentScan(
3 basePackages = "hello.core.member",
4 excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
5)
6public class AutoAppConfig {
7}
  • @ComponentScan을 설정 파일에 적용 후, basePackages 옵션을 설정해주지 않으면, 라이브러리를 포함한 해당 프로젝트 내 모든 자바 클래스를 스캔하게 되며, 이는 오랜 시간이 걸릴 수 있다
  • 따라서, 위와 같이 basePackages 옵션을 적용하여 필요한 위치에서부터 그 하위를 탐색하도록 해주자
    • 이를 지정하지 않으면, @ComponentScan이 적용된 설정 정보 클래스의 패키지가 시작 위치가 된다

@ComponentScan TIP

  • @ComponentScan에 패키지 위치를 지정하지 않고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두고 쓰면 좋다
  • 스프링 부트 사용 시에는 스프링 부트의 시작 설정 정보인 @SpringBootApplication을 프로젝트의 시작 루트 경로에 두고 사용하게 되며 해당 설정 내부에 @ComponentScan이 존재한다

컴포넌트 스캔의 기본 대상

컴포넌트 스캔은 @Component 어노테이션이 적용된 클래스를 대상으로 하며, 아래와 같은 어노테이션 또한 내부적으로 @Component 어노테이션을 가지고 있으므로 컴포넌트 스캔의 대상이 되고, 그에 따른 부가 기능도 수행한다:

  • @Controller: 스프링 MVC 컨트롤러로서 인식
  • @Service: 스프링 비즈니스 로직 구현
  • @Repository: 스프링 데이터 접근 계층으로서 인식 및 데이터 계층의 예외를 스프링 예외로 변환
  • @Configuration: 스프링 설정 정보로서 인식 및 스프링 빈의 싱글톤 유지를 위한 처리 수행

참고로, 자바 어노테이션에는 상속 관계가 존재하지 않는다. 특정 어노테이션 하위에 존재하는 어노테이션을 인식하는 것은 스프링이 지원하는 기능.


참고: Inflearn - 김영한님 강의(스프링 핵심 원리 기본편)