@Import 어노테이션은 특정 빈들이 스프링 컴텍스트 구성 정보에 포함됨을 명시하기 위해서 사용한다.
위와 같이 @EnableAutoConfiguration 어노테이션은 여러가지 메타 어노테이션을 포함하는데, 이 중에서 Import(Concreate.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 라는 빈 또한 관리되도록 추가한 것
public class FooImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"my.selector.FooConfig"};
}
}
위와 같이 설정하면 해당 ImportSelector가 리턴하는 String[] 로 Import 들을 관리 할 수 있다.
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]);
}
}
해당 스태틱 매서드의 설명은 위와 같다.
밑줄 친 부분의 경로에 있는 후보들을 (빈들을) load 한다고 명시되어 있다.
해당 경로에 package 정보를 포함한 클래스의 경로를 입력 해 주면, (IntelliJ 에서 작성 시 자동완성도 지원한다) 해당 클래스들이 load() 매서드에 의해 로드되어 주입되게 된다.
@SpringBootApplication 어노테이션에 포함되어 있고, 일반적인(작은) 어플리케이션에서는 해당 어노테이션으로만 사용한다.
package 레벨에서 @Component, @Configuration 어노테이션으로 선언된 스프링 빈들을 스캔해서 스프링 컨텍스트에 등록하는 역할을 담당한다. 일반적으로 @SpringBootApplication 어노테이션이 붙은 메인 클래스(~application.java) 가 메인 패키지 바로 아래에 있다는 점을 생각하면, 우리가 어플리케이션을 조작하면서 새로 선언하게 되는 모든 스프링 빈들이 이 컴포넌트 스캔에 의해서 관리된다는 점을 알 수 있다.
위 글에서는
...
@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 를 컴포넌트 스캔과 합쳐서 사용하는 방법도 소개되었는데, 위 방법도 재미있다. (물론 프로젝트 크기가 엄청나야지 좀 쓸 만 할 것 같다.)