@Import vs @ComponentScan

Kim Dong Kyun·2024년 2월 11일
0

1. @Import

@Import 어노테이션은 특정 빈들이 스프링 컴텍스트 구성 정보에 포함됨을 명시하기 위해서 사용한다.

위와 같이 @EnableAutoConfiguration 어노테이션은 여러가지 메타 어노테이션을 포함하는데, 이 중에서 Import(Concreate.class) 부분을 살펴 보자.

2 - 1 @Import(value = {Concreate1.class, Concrete2.class...})

일반적으로 사용하는 방법으로, 우리가 @Configuration 클래스에 사용할 @Bean 들을 모아서 한번에 컴포넌트 스캔 할 수 있도록 관리하는 것과 동일하게 (한 컨피규레이션 클래스에서 여러 빈들을 컴포넌트 스캔 대상이 될 수 있도록 조정 가능하게 할 수 있는 것과 같이 ) value 에 여러 클래스들을 명시해서 해당 클래스들이 스프링 컨텍스트에서 관리되게 하는 방식이다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@DataJpaTest // JPA Repoository TEST
@ExtendWith(SpringExtension.class) // JUnit 등 Spring Extends enable
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Import(value = {Repositories.class, RepositoryFactory.class, TestQueryDslConfig.class})
public @interface AutoConfigureRepositoryTest {
}
  • 이번에 DataJpa 테스트를 진행하면서 Jpa와 무관한 QueryDSL 의 컨피규레이션을 테스트 레벨에서도 선언 해 줄 필요가 있었는데, 해당 부분을 위에 명시한 "TestQueryDslConfig.class" 으로 빈 등록시키고, 해당 어노테이션을 실제 테스트 클래스에 적용함으로써 테스트 클래스에서는 테스트 환경을 일일이 신경쓰지 않아도 테스트가 가능하게 만들었다.

  • @DataJpaTest 는 JPA 관련 테스트(슬라이스 테스트)를 위한 환경을 제공한다. 따라서 컴포넌트 스캔이 아닌 JPA 관련 빈들만 등록해서 관리하게 된다.

  • 이 때 @Import 어노테이션을 통해 TestQueryDSLConfig 라는 빈 또한 관리되도록 추가한 것

1 - 1 @Import(? extends ImportSelector.class)

  • ImportSelector 인터페이스는 아래와 같이 selectImports 매서드를 가지고 있다.

  • 해당 매서드의 리턴에서 Bean 들의 정보를 리턴하면 (String[] 타입으로) 될 것이다.
public class FooImportSelector implements ImportSelector {
      @Override
      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
          return new String[]{"my.selector.FooConfig"};
    } 
}

위와 같이 설정하면 해당 ImportSelector가 리턴하는 String[] 로 Import 들을 관리 할 수 있다.

  • 그러나 위와 같은 방식도 아직 좀 찜찜하다. String 으로 관리하기엔 좀 그런 것 같다

1 - 2 @Import with 자동 구성 정보 파일

public class FooConfig implements DeferredImportSelector {
    private final ClassLoader classLoader;

    public FooConfig(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        List<String> autoConfigs = new ArrayList<>();

        ImportCandidates.load(MyAutoConfiguration.class, classLoader).forEach(autoConfigs::add);

        return autoConfigs.toArray(new String[0]);
    }
}
  • 위와 같이 ImportCandidtaes.load 매서드를 사용해서 자동 구성 정보를 불러오자.

  • 해당 스태틱 매서드의 설명은 위와 같다.

  • 밑줄 친 부분의 경로에 있는 후보들을 (빈들을) load 한다고 명시되어 있다.

  • 해당 경로에 package 정보를 포함한 클래스의 경로를 입력 해 주면, (IntelliJ 에서 작성 시 자동완성도 지원한다) 해당 클래스들이 load() 매서드에 의해 로드되어 주입되게 된다.


2. @ComponentScan

@SpringBootApplication 어노테이션에 포함되어 있고, 일반적인(작은) 어플리케이션에서는 해당 어노테이션으로만 사용한다.

package 레벨에서 @Component, @Configuration 어노테이션으로 선언된 스프링 빈들을 스캔해서 스프링 컨텍스트에 등록하는 역할을 담당한다. 일반적으로 @SpringBootApplication 어노테이션이 붙은 메인 클래스(~application.java) 가 메인 패키지 바로 아래에 있다는 점을 생각하면, 우리가 어플리케이션을 조작하면서 새로 선언하게 되는 모든 스프링 빈들이 이 컴포넌트 스캔에 의해서 관리된다는 점을 알 수 있다.

  • 위와 같이 main.java.~ 패키지에 존재하는 모든 클래스들은 ComponentScan 의 대상이 되는 것이다.

3. 그렇다면 두 개의 차이는 무엇인가?

Baeldung : @Import vs @ComponentScan

위 글에서는

...
@Import is very similar to @ComponentScan, except for the fact that @Import has an explicit approach while @ComponentScan uses an implicit one.

라고 설명한다.

즉, @ComponentScan 은 패키지 내에 있는 모든 빈들을 스프링이 자동으로 스캔해서 스프링 컨텍스트로 활용하고, (이 방법은 implicit == 암묵적이다)

@Import 어노테이션은 개발자가 직접 컨텍스트에서 관리할 빈들을 명시한다 (이 방법은 explicit == 명시적이다)

더불어서 위 밸덩 글에서 @import 를 컴포넌트 스캔과 합쳐서 사용하는 방법도 소개되었는데, 위 방법도 재미있다. (물론 프로젝트 크기가 엄청나야지 좀 쓸 만 할 것 같다.)

0개의 댓글