[spring] 컴포넌트 스캔 (스프링 기본편 by 김영한)

su_y2on·2022년 1월 24일
0

Spring

목록 보기
20/30
post-thumbnail

컴포넌트 스캔


지금까지는 스프링 빈을 설정정보AppConfig)를 통해 등록했습니다. 이렇게 할 경우 스프링 빈이 많아질 수록 설정 정보가 커지고 반복도 많아집니다. 따라서 설정 정보 없이 스프링 빈을 등록하는 방식이 있는데 그것이 바로 컴포넌트 스캔입니다. 물론 의존관계도 설정정보없이 등록할 수 있도록 기능을 제공해줍니다.





컴포넌트 스캔을 적용하기 위해 새로운 설정 정보 클래스 AutoAppConfig를 만들어보겠습니다. @ComponentScan을 붙여주면 컴포넌트 스캔을 사용한다고 스프링이 인식합니다.필요하지 않은 스프링빈들은 등록하지 않기 위해 excludeFilter를 적용해서 필터링을 설정해주었습니다. (필수적인 것 아님!!)

    @Configuration
    @ComponentScan(
            excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
    Configuration.class))
    public class AutoAppConfig {
    
    }




컴포넌트 스캔은 @Component가 붙은 클래스를 스프링빈으로 등록해줍니다. 따라서 등록하고싶은 구현체 클래스에 @Component를 붙여주도록 하겠습니다.



먼저 repository는 memorymember로 정해주고 discountpolicy는 rate로 적용해주기 위해 각각의 클래스에 어노테이션을 달아줍니다.

  @Component
  public class MemoryMemberRepository implements MemberRepository {}
  @Component
  public class RateDiscountPolicy implements DiscountPolicy {}

MemberService의 구현체에도 component 어노테이션을 달아주고 의존성을 주입해주기 위해 생성자위에 @Autowired를 달아줍니다. 이렇게 하면 MemberRepository와 타입이 일치하는 스프링빈을 찾아서 주입해줍니다. 지금은 MemoryMemberRepository가 그것입니다.

  @Component
  public class MemberServiceImpl implements MemberService {
  
      private final MemberRepository memberRepository;
      
      @Autowired
      public MemberServiceImpl(MemberRepository memberRepository) {
          this.memberRepository = memberRepository;
      }
}

OrderService에도 같은 원리로 등록해줍니다.

  @Component
  public class OrderServiceImpl implements OrderService {
  
      private final MemberRepository memberRepository;
      private final DiscountPolicy discountPolicy;
      
      @Autowired
      public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
          this.memberRepository = memberRepository;
          this.discountPolicy = discountPolicy;
      }
}

컴포넌트 스캔 후 스프링 빈 저장소는 아래와 같습니다. 빈 이름은 기본적으로 클래스이름에서 앞글자만 소문자로 바꿔서 등록됩니다.




컴포넌트 스캔 범위

컴포넌트 스캔의 범위는 일반적으로 설정파일(AutoAppConfig)의 위치부터 탐색합니다. 따라서 모든 프로젝트에 있는 컴포넌트를 스캔하고 싶다면 최상위에 설정파일을 만들면 됩니다. 만약 탐색위치를 정해주고싶다면 아래와 같이 basePackages 옵션을 이용해서 설정가능합니다. 지금같은 경우 hello.core를 포함해 하위 패키지를 모두 탐색합니다.

@ComponentScan(
          basePackages = "hello.core",
}




컴포넌트 스캔 대상

컴포넌트 스캔의 대상은 @Component를 포함한 모든 대상입니다. 따라서 @Controller, @Service, @Repository, @Configuration도 스캔의 대상이 됩니다. 이 어노테이션들은 모두 @Component를 포함하고있기 때문입니다. 여기서 주의할 점은 어노테이션은 상속관계를 갖지 않습니다. 특정 어노테이션이 다른 어노테이션과 함께 있다는 것은 java언어가 아닌 스프링이 제공해주는 기능입니다.




중복 등록과 충돌

만약 같은 이름의 스프링 빈이 등록되면 어떻게 될까요?


먼저 자동등록 빈과 자동등록 빈끼리 이름이 같은 경우에는 빈을 호출할 경우 아래와 같은 에러가 납니다.

ConflictingBeanDefinitionException




수동등록 빈과 자동등록 빈이 충돌 했을 때는 수동 등록빈이 자동등록빈을 오버라이딩 하며 우선권을 갖게 됩니다. 그리고 어떤 것이 어떤것으로 대체되었는지 로그로 알려줍니다 (친절한 스프링👍)

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

그렇지만 엄청 많은 스프링 빈을 관리하면서 개발자가 의도한게 아니라면 오히려 이러한 오버라이딩은 디버깅을 어렵게 만듭니다...


따라서 스프링 부트는 아래와 같은 에러를 내기로 기본값을 변경했습니다. 에러메세지가 참 너무 친절합니다.

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

만약 오버라이딩을 하고싶다면 setting(application.properties)에 spring.main.allow-bean-definition-overriding값을 true로 설정하라고 알려도 줍니다. 실제로 저렇게 세팅해주면 오류가 나지 않고 수동 등록빈이 우선권을 갖게 됩니다!

0개의 댓글