-
[04] ApplicationContext - @Component와 Component ScanningSpring/Spring 핵심 기술 2020. 4. 14. 09:16반응형
Component scanning은 @Component(@Controller, @Service, @Repository, @Configuration)가 붙어있는 class를 찾아서 bean을 생성하여 application context에 등록하는 작업을 말합니다.
@ComponentScan을 사용하여 원하는 package로 부터 component scanning을 할 수 있습니다. @ComponentScan 어노테이션의 basePackageClasses 옵션에 class file을 지정하면 BeanFactoryPostProcessor를 상속 받는 ConfigurationClassPostProcessor에 의해 class file이 위치한 package 부터 하위의 모든 package에 대해 component scanning을 수행하게 됩니다. component scanning은 functional한 bean등록 또는 @Bean을 사용한 bean등록 작업이 진행되기 전에 실행됩니다.
Component scanning은 @ComponentScan이 어 있는 Configuration부터 시작하게 되는데, Spring boot Application의 entry class에 붙이는 @SpringBootApplication 어노테이션은 @ComponentScan을 포함하고 있습니다. 그렇기 때문에 일반적인 경우 entry class의 package로 부터 하위의 모든 package에 대해 component scanning을 수행합니다.
Component scanning 범위 밖에 @Component 어노테이션을 붙여 bean으로 등록하고자 해도 IoC container에 등록되지 않으니 주의해야 합니다.
다음은 component scanning 범위 밖에서 @Component을 사용하여 bean 등록을 시도하는 예입니다.
1. Entry class의 package 밖에 별도의 myservice package를 생성하고 MyService.java를 만듭니다.
2. @Service 어노테이션을 사용하여 MyService를 bean으로 등록합니다.
import org.springframework.stereotype.Service; @Service public class MyService { }
3. 범위 밖에서 생성한 MyService를 component scanning 범위 안에서 주입합니다.
@SpringBootApplication public class Application { @Autowired MyService myService; public static void main(String[] args) { SpringApplication.run(Application.class); } }
위의 코드를 실행할 경우 MyService bean을 찾을 수 없어 주입할 수 없다는 오류가 발생함을 확인할 수 있습니다.
만약 bean 주입에 실패하는 경우 먼저 component scanning 범위를 확인 확인할 필요가 있습니다.
[Spring Application을 만드는 방법]
1. Spring boot의 SpringApplication.run() static method를 사용하는 방법
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
2. Builder를 사용한 방법
3. SpringApplication Instance를 만들어 사용하는 방법 (Functional한 bean 등록 방법)
Application 구동 시간이 짧아야 할 경우 function을 사용한 bean 등록 방법을 고려할 수 있습니다. 이 방법은 Reflection이나 Proxy기반의 CG library를 사용하지 않아 application 구동 시 성능 상의 잇점이 있습니다. 이 방법을 코드로 확인해보면 다음과 같습니다.
SpringApplication 객체를 생성할 때 생성자의 매개값으로 application entry class를 전달하여 생성합니다.
SpringApplication app = new SpringApplication(Application.class);
SpringApplication 객체의 addInitiallizers() method를 통해 원하는 type의 application context bean을 주입받을 수 있습니다. 또한 application context를 주입 받을 때 application context bean의 registerBean() method 사용하여 매개값으로 지정한 class를 bean으로 등록할 수 있습니다.
app.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext -> { applicationContext.registerBean(MyService.class); applicationContext.registerBean(ApplicationRunner.class, () -> args1 -> System.out.println("Functional Bean Definition")); });
초기화를 마친 SpringApplication 객체의 run() method를 실행하여 spring application을 구동합니다. funtional한 bean 등록에 대한 전체 예제 코드는 다음과 같습니다. component scanning 범위 밖의 bean을 functional한 방법으로 context에 등록하는 부분을 눈여겨 봐주시기 바랍니다.
@SpringBootApplication public class Application { //4.성공적으로 Bean이 주입되었는지 확인. @Autowired MyService myService; public static void main(String[] args) { //1.SpringApplication 객체를 생성하고 SpringApplication app = new SpringApplication(Application.class); //2.어플리케이션 초기화 //원하는 ApplicationContext를 주입할 수 있으며 GenericApplicationContext를 주입했다. //GenericApplicationContext의 registerBean()을 사용하여 직접 Bean을 등록할 수 있다. //여기서는 Compnent Scan 범위 밖의 class를 Bean으로 등록해보고자 한다. app.addInitializers((ApplicationContextInitializer<GenericApplicationContext>) applicationContext -> { //Component Scanning 범위 밖의 Bean을 등록 applicationContext.registerBean(MyService.class); applicationContext.registerBean(ApplicationRunner.class, () -> args1 -> System.out.println("Functional Bean Definition")); }); //3.run() 메소드로 어플리케이션을 실행한다. app.run(args); } }
위의 코드를 실행해보면 Component scanning 범위 밖의 bean도 원하는대로 application context에 등록 되었음을 확인할 수 있습니다.
모든 bean을 functional하게 등록하는 것은 매우 비효율적이므로 Component scanning을 통해 올릴 시 초기 application 기동에 비용이 크게 발생할 bean만 @Bean을 붙인 뒤 functional하게 등록하고 나머지 bean들은 @Component 계열의 어노테이션을 사용하여 자동으로 등록하는 것이 바람직하다.
'Spring > Spring 핵심 기술' 카테고리의 다른 글
[06] ApplicationContext - EnvironmentCapable interface (0) 2020.04.16 [05] ApplicationContext - Bean의 scope (0) 2020.04.14 [03] ApplicationContext - @Autowired를 통한 Dependency Injection (0) 2020.04.13 [02] ApplicationContext - 다양한 Bean 설정 방법 (0) 2020.04.12 [01] Inversion of Control(IoC) 컨테이너와 Bean (0) 2020.04.12