Spring의 ComponentScan

Hansu Kim·2022년 2월 18일
0

Spring boot

목록 보기
4/10

스프링의 ComponentScan은 @Component가 붙은 클래스들을 자동으로 빈으로 생성해준다.

컴포넌트 스캔을 사용하면 @Configuration이 붙은 클래스도 자동으로 빈으로 생성해주므로 주의하여야 한다.

@Configuration을 통해 수동으로 빈을 등록해줄 경우, @Bean을 메소드에 매핑하여 의존성을 주입해줬다.
하지만 Component Scan에선 클래스의 생성자에 @Autowired를 통해 의존성을 주입해준다.

Component Scan으로 생성되는 빈의 이름

Component Scan으로 생성되는 빈은 기본적으로 클래스명을 사용하되, 맨 앞글자를 소문자로 사용한다.

@Component
class ComponentTest(){} -> componentTest

@Component("testA")
class ComponentTest(){} -> testA

스캔시 탐색 위치

컴포넌트 스캔시 기본 전략은 @Component 어노테이션이 작성된 클래스들을 타입별로 생성해준다는 것이다.
스캔시, 탐색 시작 위치를 지정해줄 수 있다.

@Configuration
@ComponentScan(
        basePackages = {"group.core.member", "group.core.service},
        basePackageClasses = FixDiscountPolicy.class,
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
                classes=Configuration.class)
)

위와 같이, basePackages를 지정해주면 해당 패키지를 포함해서 하위 패키지를 모두 탐색한다.

혹은 basePackageClasses로 클래스를 지정할 경우, 지정한 클래스의 패키지를 시작 위치로 지정하게 된다.

만약 지정하지 않을 경우, @ComponentScan이 붙은 클래스가 패키지의 시작 위치가 된다.

스캔시 탐색 대상

Spring에서 컴포넌트 스캔시 탐색되는 대상은 기본적으로 아래와 같다.

  • @Component: 스프링 설정 정보로 인식하고 싱글톤을 보장해준다.
  • @Controller: Spring MVC Controller로 인식
  • @Service: 비지니스 로직 클래스로, 특별한 처리를 하지 않는다.
  • @Repository: 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환
  • @Configuration

Controller, Service 등의 어노테이션의 코드를 직접 확인해보면 @Component가 포함되어있는 것을 알 수 있다.

참고로, 어노테이션에는 상속 관계가 없다. 따라서 어느 어노테이션이 내부적으로 다른 어노테이션을 가지고 있다는 것은 언어적으로 지원되는 기능이 아니라 Spring에서 지원해주는 기능이다.

권장하는 스캔 방법

권장되는 스캔 방식은 패키지의 위치를 지정하지 않고, 설정 정보 클래스를 프로젝트 최상단에 두는 것이다.

스프링 부트에선 @SpringBootApplication을 프로젝트 최상단에 위치시키는 것이 관례이며, 해당 어노테이션 안에 @ComponentScan이 포함되어있다.

참고 - @SpringBootApplication의 ComponentScan 어노테이션 상세 옵션

@ComponentScan(
	excludeFilters = { 
    	@Filter(
        	type = FilterType.CUSTOM,
            classes = TypeExcludeFilter.class
        ),
		@Filter(
        	type = FilterType.CUSTOM,
            classes = AutoConfigurationExcludeFilter.class
        )
    }
)
public @interface SpringBootApplication 

@ComponentScan.Filter(type, value, classes, pattern)

컴포넌트 스캔시, Filter의 type을 통해 포함하거나 제외할 타입들을 세부 정의할 수 있다.
보통 type 파라미터에 enum 클래스인 FilterType만이 올 수 있으며, FilterType은 아래 5가지 enum을 가지고 있다.

  • ANNOTATION: 기본값. 어노테이션을 인식해 동작
  • ASSIGNABLE_TYPE: 지정한 타입과 자식 타입을 인식해 동작
  • ASPECTJ: AspectJ 패턴 사용
  • REGEX: 정규 표현식
    • ex) org\.example\.Default.*
  • CUSTOM: TypeFilter라는 인터페이스를 구현해서 처리

    참고- 사실 @Component면 충분하기 때문에 includeFilters는 사용할 일이 거의 없으며, excludeFilters는 여러 이유로 간혹 사용되지만 많지는 않다.

만약 컴포넌트 스캔에서 같은 빈 이름을 등록하면 어떻게 될까?

같은 빈 이름이 등록되는 케이스는 아래와 같이 나눌 수 있다.
1. 자동 빈 등록 vs 자동 빈 등록
2. 자동 빈 등록 vs 수동 빈 등록

자동 빈 등록 vs 자동 빈 등록

ConflictingBeanDefinitionException 예외 발생

자동 빈 등록 vs 수동 빈 등록

자동 빈 등록에서는 클래스명의 맨 앞글자를 소문자로 변환한 뒤 빈의 이름으로 사용한다.
만약 @Bean(name="XXXX")을 통해, 이미 자동으로 등록된 빈의 이름이 수동 등록된 경우, 로그 출력 후 수동 빈이 자동 빈을 오버라이딩 하도록 동작한다.

하지만 실무에선 해당 로직으로 인해 설정이 꼬이는 경우가 발생하는 경우가 많았기에, 최근 스프링 부트에선 수동 빈 등록과 자동 빈 등록에서 충돌이 발생할 경우 오류가 발생하도록 기본 값을 바꾸었다.

해당 로직은 @SpringBootApplication이 포함된 CoreApplication Class를 구동해보면 확인할 수 있다.

만약 빈 충돌 발생시 자동 오버라이딩 기능을 활성화하고 싶다면 application.properties에 "allow-bean-definition-overriding=true" 항목을 추가해주면 된다.

0개의 댓글