스프링

Spring boot AutoConfiguration 동작방식2 - DeferredImportSelector

du.study 2021. 1. 10. 17:00
728x90

저번포스팅에서 SpringBoot의 AutoConfiguration 동작에 대해서 기록한 적이 있습니다.

 

duooo-story.tistory.com/52

 

Spring boot Auto Configuration 동작방식

이전에도 간략하게나마 Spring boot의 동작 원리를 작성한 적이 있습니다. 이번에는 그 과정을 좀 더 자세하게 기록해 보고자 합니다. 결론부터 말하면 스프링 부트는 두가지 방법으로 bean 등록을

duooo-story.tistory.com

 

이번에는 이 동작을 이용해서 직접 해당 코드를 작성해보려합니다.

우선 ComponentScan을 제외한 빈 등록 방식을 사용하기 위해서는 다음과 같은 조건을 충족 클레스가 필요합니다.

- import를 통해 불러와지며 ( @Component를 가지고 있지 않아도 됌) DeferredImportSelector , DeferredImportSelector.Group을 구현하고 있는 클래스

 

그럼 코드를 구현해보겠습니다.

우선 안되는 경우를 위한 테스트를 위해 다음과같이 파일을 구성했습니다.

 

@Component
public class TestRunner  implements ApplicationRunner {
    @Autowired
    public AnotherComponent anotherComponent;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        anotherComponent.printTest();
    }
}


@SpringBootApplication
public class WebTestApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebTestApplication.class, args);
    }
}

@Component
public class AnotherComponent {
    public void printTest(){
        System.out.println("hello");
    }
}

 

이 경우, 우선 Main으로 실행되는 클래스 경로 외의 다른 패키지의 bean을 등록하려하기때문에 다음과 같은 에러가 발생합니다.

Field anotherComponent in com.example.webtest.TestRunner required a bean of type 'com.example.another.AnotherComponent' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)

자 이제 AnotherComponent를 빈으로 등록하도록 webtest패키지 아래 Configuration하나를 추가로 생성해 보겠습니다.

@Configuration
@Import(TestConfiguration.MyImportSelector.class)
public class TestConfiguration {
    public static class MyImportSelector implements DeferredImportSelector,DeferredImportSelector.Group{
        private AnnotationMetadata meta;
        @Override
        public String[] selectImports (AnnotationMetadata importingClassMetadata) {
            return new String[]{MyImportSelector.class.getName()};
        }

        @Override
        public Class<? extends Group> getImportGroup() {
            return MyImportSelector.class;
        }

        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            meta = metadata;
        }

        @Override
        public Iterable<Entry> selectImports() {
            List<Entry> list = new ArrayList<>();
            list.add(new Entry(meta, AnotherComponent.class.getName()));
            return list;
        }
    }
}

우선 Import를 통해서 클레스를 등록해야합니다. Import를 통해 불러와진 클레스중, DeferredImportSelector를 구현한 클레스에서 getImportGroup을 호출을 통해 DeferredImportSelector.Group 메서드를 호출할 클레스 정보를 가져오게 됩니다.

 

이후, process 호출 후, selectImports 과정에서 bean으로 등록할 클레스의 이름을 가져오게 되고, 이속에서 Condition관련 어노테이션 설정 확인 후, bean등록을 하게됩니다.

 

결과적으로 TestRunner에서는 정상적으로 print가 호출된것을 볼 수 있습니다.

예제 코드 : github.com/pesang72/spring-boot-example/tree/master/src/main/java/du/study/springboot/autoconfiguration

728x90