Spring/Spring Study

[Inflearn] 컴포넌트 스캔과 자동 의존 관계 주입

모모토 2021. 8. 20. 17:59
반응형

※ 본 포스팅은 Inflearn - 김영한 강사님의 '스프링 핵심 원리 - 기본편' 을 참고,공부하여 만들었습니다.

 

이 주제를 선택한 이유 , 개발자의 고충

스프링 빈을 등록할 때 자바 코드의 @Bean , XML 의 등을 통하여 스프링빈을 등록한다.

 

 

 

Singleton 에서 참고로 사용했던 'AppConfig' 라는 DI컨테이너 내부에 '개발자'가 설정정보를 입력하여 , 빈을 등록하고 의존관계를 주입하였다. 예제차원에서는 빈으로 등록할 개수는 많지 않았기에 AppConfig에 빈과 의존관계를 주입하는게 어렵지는 않았다.

 

하지만, 실제 서비스 수준에서 생각해볼때 모든 객체를 AppConfig에 설정정보를 입력하여 관리하는게 과연 효율적일까?

 

만약 AppConfig에 등록해야할 객체를 누락한다면 ? 무엇보다 개발자는 반복작업을 싫어하기 때문에 이러한 방법은 실제론 사용하기 난감하다. 그래서 Spring은 이러한 수고를 덜어주기 위하여 ComponentScan 이라는 기능을 제공한다.

 

@ComponentScan , @Component & @Autowired

 

@ComponentScan은 스프링 3.1부터 도입된 Annotation이며 스캔 위치를 설정하고, 어떤 Annotation을 스캔할지 또는 하지 않을지 결정하는 Filter 기능을 가지고있다.

 

코드를 먼저 살펴보면서 위의 세가지 애노테이션으로 어떻게 컨테이너를 만들고 설정하고 의존관계를 주입하는지 알아보자

 

 

1. 스프링컨테이너에 필요한 설정정보가 담길 AutoAppConfig 클래스 생성후 @ComponentScan 을 붙여준다.

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.springframework.context.annotation.ComponentScan.*;
@Configuration
@ComponentScan
public class AutoAppConfig {
 
}

 

2. 빈으로 등록할 클래스에 @Component & @Autowired로 의존관계 주입

@Component
public class MemberServiceImpl implements MemberService {
 
 private final MemberRepository memberRepository;

 @Autowired // 생성자에 Autowired를 붙여 자동으로 의존관계 주입 ,생성자 주입
 public MemberServiceImpl(MemberRepository memberRepository) {
 this.memberRepository = memberRepository;
 }

//... 생략

}

 

Component를 이용한 스프링 컨테이너 생성과정

 

따로따로 존재하는 컴포넌트를 스캔하여 설정정보 구성후 스프링컨테이너에 등록

 

여러개의 파라미터도 알아서 의존관계 주입

 

자동 의존 관계 주입

의존 관계 주입 방법엔 생성자 주입 , 수정자 주입 , 필드 주입 , 일반 메서드 주입 4가지가 존재

이중에서 생성자 주입만 설명하겠다. 왜냐하면 다른 방법들은 실제로 추천되지 않는 방법이기 때문,

생성자 주입의 장점

  1. 생성자 호출시점에 딱 한번만 호출을 보장 (Singleton , 불변)
  2. 데이터 입력이 누락 되었을때 , 컴파일 단계에서 오류를 감지해낸다.(★★★★★)

생성자 주입

 


수정자 주입

 

수정자를 호출하여 repository를 주입해야함

 


3. final 을 사용 가능 하게하여 생성자 내부요소 누락에 대하여 사전에 컴파일오류를 발생시킴

 

 

결론 : Autowired 로 의존관계를 주입할때 생성자 주입을 선택하면 컴파일 단계에서 오류를 발견하여 큰 버그가 오는걸 방지할 수 있다.

 

컴포넌트 스캔 범위

1) @ComponentScan의 디폴트 범위

@ComponentScan 의 디폴트 범위는 @ComponentScan Annotation이 붙어있는 설정 정보 클래스가 소속된 패키지부터 스캔한다.

이러한 디폴트 스캔을 원하지 않는다면 스캔 범위를 조정할 수 있는 방법들이 존재하는데

 

1. basePackages - 직접 패키지를 지정하여 탐색

 

 

2. basepackageClasses - 지정한 클래스가 소속된 패키지부터 탐색

 

 

3. filter 를 이용한 스캔대상 추가 및 제외 (잘 사용되진 않음)

  • includeFilter - 컴포넌트 스캔 대상을 추가로 지정한다.
  • excludeFilter - 컴포넌트 스캔에서 제외할 대상을 지정한다.

 

2) 일반적으로 권장되는 방법

 

 

탐색위치에 대한 별도의 설정을 하지않고 @ComponentScan이 붙은 설정클래스를 프로젝트의 최상단(hello.core)에 두는것이다. 그러면 디폴트 탐색범위에 의해 프로젝트 최상단에서 부터 하위패키지로 이동하며 전체를 스캔하게 된다.

 

 

중복 등록과 충돌

 

ComponentScan을 이용하여 빈으로 등록후 , DI 컨테이너(AutoAppConfig)에도 중복으로 등록하면? 전자를 자동 빈 등록 , 후자를 수동 빈 등록 이라고 한다. 일반적으로 수동으로 등록한 빈을 우선시한다. 따라서 수동빈이 자동빈을 오버라이딩 해버리면서 다음과 같은 로그를 남김

 

Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing

 

위와 같은 결과를 개발자가 의도해서 도출했다면 그것은 잘된일이지만 , 보통은 그렇지 않다. 위와 같은 상황에서 개발을 진행할시 추후 겉잡을 수 없는 버그를 초래한다고 한다.(정확히 무슨버그인지는 모르겠음)

최근 스프링부트에서는 자동 빈과 수동 빈이 충돌할 시 오류를 발생시키도록 변경이 되었다고 한다.

결론은 빈의 충돌이 일어나지 않도록 설계를 잘하자! (스프링 부트를 쓰자?)

 

SpringBoot에서의 @ComponentScan 범위

참고로 이 예제를 실행하면서 사용한 Springboot 에서는 @SpringBootApplication 이 붙은 Application 클래스를 프로젝트의 최상단에 두는게 관례이다. 왜냐하면 @SpringBootApplication Annotation에 @ComponentScan이 포함되어 있기 때문

 

01

 

Q. 그렇다면 SpringBoot 는 DI컨테이너가 필요없는것일까??

 

 

정리

Springframework의 핵심기술중 하나인 @ComponentScan 을 이용하여 별도의 설정정보의 필요없이 자동으로 스프링 빈을 등록할 수 있게 됨.

또한 의존관계도 자동으로 주입시켜주는 @Autowired 기능도 제공 , 단 의존 관계 자동 주입시 생성자 주입 형태를 선택하여 , 순수한 자바 코드만으로 테스트코드를 작성할 수 있게 해주는게 중요하다.