프레임워크/Spring

[Spring] @ComponentScan이란 무엇인가?

멍토 2021. 6. 24.

우리는 프로그램을 만들면서 필요에 따라 빈을 등록한다.

@Component, @Service, @Repository, @Controller, @Configuration 등등이 있다.

그렇다면 우리가 등록한 것들을 어떻게 찾아서 빈으로 등록을 해줄까?

우리가 등록하고 싶다고 설정한 빈들을 찾아서 빈으로 만들어 주는 친구가 바로 @ComponentScan 이다.

@ComponentScan은 해당 애너테이션이 작성된 패키지 이하의 클래스들을 순회하며 빈으로 등록될 객체들을 탐색한다.

아래와 같이 기준이 될 패키지를 지정할 수도 있다.

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    // ...
}

SpringBoot를 사용한다면 우리가 따로 설정하지 않는이상 @ComponentScan을 보기가 힘들다.

그 이유는 @SpringBootApplication이 @ComponentScan을 들고있기 때문이다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    // ...
}

위에서 볼 수 있듯이 포함시키지 않을 것들을 따로 설정을 할 수 있다.

혹은 따로 스캔하지 않을 부분에 @IgnoreDuringScan을 붙여서 처리할 수도 있다.

@Service
@IgnoreDuringScan
public class ActorService {
  // ..
}

이와 반대로 포함 시킬것들을 설정하는 방법도 있으며 패턴적용까지 가능하다.

ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    ...
}

어차피 등록될 부분은 등록될텐데 includeFilter를 왜 적용하는지 필요성을 아직 느끼지는 못했다.

그래서 여러가지 테스트를 해보았다.

exclude를 패턴으로 적용하고 거기서 걸리는 부분을 include로 포함시키지 않을까? 라고 생각해보았는데 아래와 같이 실행하니 되지 않았다.

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
    @Filter(type = FilterType.REGEX, pattern = ".*[vice]")},
    includeFilters = {@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = LineService.class)})
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

 

@Test
void componentScan() {
    ApplicationContext context = getApplicationContext();

    LineService service = context.getBean("lineService", LineService.class);
    assertThat(service).isNotNull();
}

해당 패키지가 아닌 다른 패키지에 있는 친구들을 등록할 수 있는지 테스트를 해보았는데 실패하였다.

public class TestService {
    // 다른 패키지의 서비스
}

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),},
    includeFilters = {@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = TestService.class)})
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

컴포넌트가 붙어있지 않아도 되는지 테스트를 해보았다.

public class LineService {
}

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),},
    includeFilters = {@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = LineService.class)})
public class HelloApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

등록이 잘 되었다.

해당 패키지에서 지정한 친구들만 등록된다.

-> 포함되어 있는 모든 빈들이 등록된다.

출처 : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-autodetection

https://www.baeldung.com/spring-componentscan-filter-type

댓글

💲 광고입니다.