Spring

[Spring] 컴포넌트 스캔(Component Scan)

cloud-grace 2024. 6. 8. 16:04

컴포넌트 스캔

@Component를 가진 모든 대상을 가져와 Bean에 등록하기 위해 찾는 과정을 말한다. 즉, 빈 설정 파일과 @Bean을 통해 빈을 일일이 지정할 필요가 없다. 즉, 명시적인 등록과 달리, 자동으로 빈을 등록하는 방법이다.
 

1. xml 파일에 설정하기

<context:component-scan base-package="com.dx" />

 

2. Java 파일 안에서 설정하기 (*실무에서 많이 쓰는 방법)

@Configuration
@ComponentScan(basePackages = "com.dx")
public class AppConfig {
}

 

컴포넌트 스캔 대상

  • @Component : 컴포넌트 스캔에서 사용한다.
  • @Controller : Spring MVC Controller에서 사용한다.
  • @Service : Spring Business 로직에서 사용한다.
  • @Repository : Spring Data Access 계층에서 사용한다.
  • @Configuration : Spring 설정 정보에서 사용한다.
  • 아래 클래스 코드를 살펴보면 @Component를 포함하고 있다. (*이 상속 관계는 Spring 지원 기능, Java의 문법 X)
@Component
public @interface Controller {

}

@Component
public @interface Repository {

}

@Component
public @interface Configuration {

}

 

@ComponentScan

이 어노테이션이 있는 파일의 패키지 아래를 스캔한다.
 

탐색 위치

  • basePackages를 사용하면 탐색할 패키지 시작 위치를 지정한다.
  • 이 패키지를 포함하여 하위 패키지를 모두 탐색한다.
  • basePackageClasses를 사용하면 지정한 클래스가 있는 패키지를 시작 패키지로 설정한다.
@ComponentScan (
    basePackages = "com.dx.springEx"
    // basePackages = {"com.dx.springEx01", "com.dx.springEx02"} 여러 개도 지정 가능하다.
)

 

권장 방법

구성 파일에 등록한다면 패키지 위치를 지정하지 않고, 설정 정보 클래스 위치를 Project 최상단에 둔다.
만약, Spring Boot를 사용한다면 @SpringBootApplication 안에 @ComponentScan이 포함되어 있으므로 자동으로 최상단으로 유지가 된다.
 
만약, 프로젝트 구조가 아래와 같다면,
1) com.dx
2) com.dx.service
3) com.dx.controller
4) com.dx.repository
~~~
1)번인 com.dx가 프로젝트 시작 루트이다. 이 위치에 AppConfig.java 로의 설정 정보 파일을 두고, @ComponentScan을 설정하면 된다. 결국 com.hello를 포함한 하위 패키지는 모두 컴포넌트 스캔의 대상이 된다.
 

@Autowired

이 어노테이션을 활용하여 생성자에 지정하면 스프링 컨테이너가 자동으로 조건에 맞는 Type을 찾아 의존성 주입을 자동으로 해준다.
 

필터

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

 

필터 등록 방법

1. 커스텀 어노테이션 정의

// 컴포넌트 스캔 대상에 추가
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}

// 컴포넌트 스캔 대상에서 제외
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}

 

2. 커스텀 어노테이션 대상 클래스에 적용

@MyIncludeComponent
public class ExampleA { // 컴포넌트 스캔 대상에서 추가
}

@MyExcludeComponent
public class ExampleB { // 컴포넌트 스캔 대상에서 제외
}

 

3. 컴포넌트 스캔 필터 설정

@Configuration
@ComponentScan(
    excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class),
    includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class))
static class ComponentFilterAppConfig {

}

 

4. 테스트 코드

public class ComponentFilterAppConfigTest {
    @Test
    public void filterScan() {
        
        // 애플리케이션 컨텍스트 로드
        ApplicationContext ac = new AnnotationConfigApplicationContext(
            ComponentFilterAppConfig.class);
        
        // ExampleA 타입의 빈을 가져온다.
        ExampleA exampleA = ac.getBean(ExampleA.class);
        
        // ExampleB 타입의 빈을 가져오려고 시도할 때 발생하는 예외를 캡처한다.
        Throwable throwable = Assertions.catchThrowable(() -> ac.getBean(ExampleB.class));

        // ExampleA 빈이 컨텍스트에 존재함을 확인한다.
        Assertions.assertThat(exampleA).isNotNull();
        
        // ExampleB 빈을 가져올 때 NoSuchBeanDefinitionException이 발생했음을 확인한다.
        Assertions.assertThat(throwable).isInstanceOf(NoSuchBeanDefinitionException.class);

    }

 
 
 
 

참고 자료
내용 참고 : 인프런 김영한 님의 강의 "스프링 핵심 원리 - 기본편"
https://blogshine.tistory.com/217
https://velog.io/@hyun-jii/%EC%8A%A4%ED%94%84%EB%A7%81-component-scan-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EB%8F%99%EC%9E%91-%EA%B3%BC%EC%A0%95
https://velog.io/@neity16/Spring-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8-6-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%EC%BA%94Component-Scan-DI