Spring/Spring 이야기

스프링 핵심 원리 기본편 강의 - ComponentScan과 의존 관계 자동 주입

seungdols 2024. 3. 26. 07:55

@ComponentScan

  • 자동으로 @Component가 붙은 컴포넌트를 해당 빈 등록을 해준다. (싱글톤)
  • 빈 이름은 기본전략은 MemberServiceImpl class -> memberServiceImpl로 앞의 글자만 소문자로 바꿔서 등록 해준다.
    • 이름을 수정 할 수 있음.
15:47:41.702 [Test worker] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@d049e53
15:47:41.708 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
15:47:41.758 [Test worker] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [SpringCorePrinciple_basic/core/build/classes/kotlin/main/com/inflearn/spring/core/discount/RateDiscountPolicy.class]
15:47:41.761 [Test worker] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [SpringCorePrinciple_basic/core/build/classes/kotlin/main/com/inflearn/spring/core/member/MemberServiceImpl.class]
15:47:41.762 [Test worker] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [SpringCorePrinciple_basic/core/build/classes/kotlin/main/com/inflearn/spring/core/member/MemoryMemberRepository.class]
15:47:41.763 [Test worker] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [SpringCorePrinciple_basic/core/build/classes/kotlin/main/com/inflearn/spring/core/order/OrderServiceImpl.class]
15:47:41.881 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
15:47:41.882 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
15:47:41.883 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
15:47:41.884 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
15:47:41.910 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoAppConfig'
15:47:41.913 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'rateDiscountPolicy'
15:47:42.033 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberServiceImpl'
15:47:42.042 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memoryMemberRepository'
15:47:42.044 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'memberServiceImpl' via constructor to bean named 'memoryMemberRepository'
15:47:42.045 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderServiceImpl'
15:47:42.047 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'memoryMemberRepository'
15:47:42.047 [Test worker] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Autowiring by type from bean name 'orderServiceImpl' via constructor to bean named 'rateDiscountPolicy'

테스트 코드를 돌려보면, org.springframework.context.annotation.ClassPathBeanDefinitionScanner이 동작해서 등록 해야 할
파일의 path를 찾는 것을 알 수 있다.

component scan의 base packages 지정은 굳이 하지 않고, 설정 클래스를 프로젝트 시작 위치에 두고, 쓰는게 좋다. (요즘 트렌드)

사실 이미, @SpringBootApplication 설정 안에 @ComponentScan이 있다.

스캔 기본 대상

  • @Component
  • @Service
    • 특별한 처리를 안함
  • @Repository
    • 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환 해준다.
  • @Controller
    • Spring MVC controller로 인식
  • @Configuration
    • 스프링 설정 정보로 인식, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.

filter

FilterType option

  • ANNOTATION
    • 기본 값, 애노테이션을 인식하여 동작
  • ASSIGNABLE
    • 지정한 타입과 자식 타입을 인식해서 동작한다.
  • ASPECTJ
    • AspectJ 패턴 사용
  • REGEX
    • 정규 표현식
  • CUSTOM
    • TypeFilter 인터페이스를 구현해서 처리

대세는 includeFilters는 잘 안쓰고, ExcludeFilters는 간혹 쓸 일이 있다. (실무에서 쓸 일이 좀 잦음)

중복 등록과 충돌

컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록 되는데, 그 이름이 같으면 스프링은 오류를 발생 시킨다.

ConflictBeanDefinitionException이 발생함.

수동 등록 vs 자동 등록

두 등록 방식으로 같은 이름의 빈이 등록 되는 경우, 수동 등록한 빈이 우선권을 가지게 된다. 실제 실행 메시지에서는 overriding bean definition이란 메시지를
확인 할 수 있다.

Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing [Generic bean: class [com.inflearn.spring.core.member.MemoryMemberRepository]; scope=singleton;

그런데, 요즘 추세는 같은 이름으로 등록 되면, 수동/자동 모두 충돌나면 오류가 발생하도록 기본 값이 바뀌었다.

The bean 'memoryMemberRepository', defined in class path resource [com/inflearn/spring/core/AutoAppConfig.class], could not be registered. A bean with that name has already been defined in file [SpringCorePrinciple_basic/core/out/production/classes/com/inflearn/spring/core/member/MemoryMemberRepository.class] and overriding is disabled.

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

생성자 주입

  • 수정자/필드 주입보다 생성자 주입을 쓰는게 권장 사항이다.
  • 초기 생성시 의존성이 주입 되므로, 프로그램 시작-종료까지 고정 된다. (불변)
  • 수정자 주입의 경우, public으로 열어둬야 하는데, 이는 중간에 변경 될 가능성도 충분히 있어 설계상 좋지 않다.
    • 생성자 주입을 쓸때, 컴파일 오류로 버그를 빠르게 확인 할 수 있음.
    • 필드에 final을 사용하게 되면, 생성자에서 혹시라도 값이 설정 되지 않는 오류를 컴파일 시점에 막아준다.

Bean이 충돌 하는 경우

@Autowired는 기본적으로 타입 기반으로 자동 등록을 해준다.
그런데, 타입은 같은데, 구현체가 다른 경우가 있다. 그럴때, 똑같이 빈 등록을 하게 되면 문제가 발생한다.
실제로 실행 해보면, NoUniqueBeanDefinitionException에러가 발생하게 된다.

빈이 2개 이상일 때, 해결 방법

  • @Autowired 필드명
    • 생성자 주입의 경우, 파라미터 이름을 확인 한다. 하위 구현체의 이름으로 바꾸면 해결 가능하다.
  • @Qualifier
    • 추가 구분자를 붙여주는 방법이다. 빈 이름을 변경하는 것은 아니다.
    • 만약, 이름을 넣어주고, 못찾으면 Qualifier에 넣어준 이름으로 등록된 빈을 추가로 찾게 된다.
  • @Primary
    • 우선순위를 정하는 방법

@Primary vs @Qualifier

  • 스프링은 자동보다 수동이 우선순위가 더 높다.
  • 고로, @Primary보다 @Qualifier가 우선순위가 더 높게 된다.

자동 빈 등록 vs 수동 빈 등록

  • 업무 로직 빈: 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직, 데이터 계층
    • 자동으로 빈 등록 해주는게 좋다.
  • 기술 지원 빈: 기술적인 문제나 공통 관심사를 처리 할 때 주로 사용, 공통 로그처리, 데이터베이스 연결등 업무 로직을 위한 하부 기술 또는 공통 기술
    • 수동으로 등록 해주는게 명확 하다.
반응형