Home 컴포넌트 스캔
Post
Cancel

컴포넌트 스캔

컴포넌트 스캔

지금까지 스프링 빈을 등록할 때는 자바 코드의 @Bean 이나 XML 태그의 bean 등을 통해 설정 정보에 직접 등록할 스프링 빈을 나열했다.

이렇게 등록해야 할 스프링 빈이 수십, 수백개가 되면 일일이 등록하기도 귀찮고, 설정 정보도 커지고, 누락의 문제도 발생할 수 있다.

그래서 스프링은 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔이라는 기능을 제공한다.

의존관계도 자동으로 주입하는 @Autowired 도 제공한다.

컴포넌트 스캔을 사용하려면 @ComponentScan을 설정정보에 붙여주면 된다.

이 어노테이션은 @Component를 붙인 클래스에 가서 알아서 가져오는 역할을 한다.

1
2
3
4
5
6
7
8
9
@Configuration
@ComponentScan(  
	excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)  
)
public class AutoAppConfig{
} // 내용은 아무것도 없다.
//우리가 전에 만든 AppConfig에는 @Configuration이 붙어있고 
//해당 어노테이션을 타고올라가면 @Component가 존재하기 때문에 일단 테스트코드에서는 필터링 해준다.
//실무에서는 이렇게 할 일 없다고 보면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//MemberServiceImpl

@Component  
public class MemberServiceImpl implements MemberService{  
  
private final MemberRepository memberRepository;  
  
@Autowired  
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);  
}  

생성자에서 의존관계 주입을 해주고 있는데 기존의 AppConfig에서는 직접 구현체를 지정해주던 것을 해줄 수 없게 된다.

이 때 @Autowired를 써주면 자동으로 의존관계 주입을 해준다.

// MemoryMemberRepository, RateDiscountPolicy, MemberServiceImpl, OrderServiceImpl에 @Component 추가 한다.

MemberServiceImpl, OrderServiceImpl은 @Autowired도 추가해야 한다.

  • @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록.
  • 이 때 빈의 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.

  • 생성자에 @AutoWired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.

기본적으로 타입이 같은 빈을 찾아서 주입한다고 보면 된다. (getBean(MemberRepository.class)

** 그런데 현재는 MemoryMemberRepository에만 @Component를 붙였기 때문에 알아서 얘만 가져와서 등록하는데 다른 MemberRepository가 있어서 중복된다면? (ex - JdbcMemberRepository) 그럴일 없나?

탐색 위치와 기본 스캔 대상

1
2
3
4
5
6
7
@Configuration
@ComponentScan(  
	basePackages = {"hello.core.member", "hello.core.order"}
	excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)  
)
public class AutoAppConfig{
}

basePackages 로 해당 부분만 스캔할 수 있게 설정 가능하다. 위의 코드대로면 멤버패키지와 오더패키지만 스캔한다.

지정하지 않는다면? - @ComponentScan을 붙인 클래스가 있는 폴더의 하위 폴더를 다 스캔하는 것이 디폴트.

그래서 실무에서는 패키지 위치를 지정하기보단 설정 정보 클래스의 위치를 프로젝트의 최상단에 두는 것이 관례이다.

스프링 부트를 사용한다면 스프링 부트의 대표 시작정보인 @SpringBootApplication을 프로젝트 시작 루트 위치에 두는 것이 관례이다. 이 어노테이션을 타고 들어가면 @ComponentScan이 붙어있기 때문.

Component 스캔의 기본 대상

  • @Component - 컴포넌트 스캔
  • @Controller - 스프링 MVC 컨트롤러
  • @Service - 비즈니스 로직
  • @Repository - 데이터 접근 계층
  • @Configuration - 설정 정보

그래서 @Controller, @Service, @Repository 붙어있으면 알아서 빈으로 등록 되는 것이었다. 그리고 예를 들어 컨트롤러나 서비스에서 Repository가 필요할 때 @Autowired로 주입받는 것이었다.

필터

  • includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.
  • excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다.

FilterType 옵션

  • ANNOTATION : 애노테이션을 인식해서 동작한다.
  • ASSIGNABLE_TYPE : 지정한 타입과 자식타입을 인식해서 동작한다.
  • ASPECTJ : ASPECTJ 패턴을 사용
  • REGEX : 정규 표현식
  • CUSTOM : TypeFilter이라는 인터페이스를 구현해서 처리

@ Component면 충분하기 때문에, 필터기능을 사용할 일은 거의 없다. 특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 옵션을 변경하면서 사용하기 보다는 스프링의 기본 설정에 맞추어 사용하는 것이 권장된다.

중복등록과 충돌

  1. 자동 빈 등록 vs 자동 빈 등록
  2. 수동 빈 등록 vs 자동 빈 등록

자동 빈 vs 자동 빈

이름이 같은 경우 스프링은 오류를 발생시킨다.

ConflictingBeanDefinitionException 예외 발생.

수동 빈 vs 자동 빈

같은 이름으로 수동으로 등록한 빈과 자동으로 등록한 빈이 있으면 어떻게 되나.

수동 빈이 우선권을 가지고 자동 빈을 덮어쓴다.

의도하지 않았을 때 수동 빈이 덮어써서 버그가 발생하면 찾기 힘들다.

그래서 스프링부트는 최근 수동 빈 등록과 자동 빈 등록 충돌이나면 오류가 발생하도록 기본 값을 바꾸었다.

This post is licensed under CC BY 4.0 by the author.