@SpringBootApplication
=>
@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 {
@SpringBootApplication
안에는 이렇게 많은 어노테이션이 포함되어있다.
이중 @ComponentScan
을 알아보자.
@AliasFor("value")
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages} for specifying the packages
* to scan for annotated components. The package of each class specified will be scanned.
* <p>Consider creating a special no-op marker class or interface in each package
* that serves no purpose other than being referenced by this attribute.
*/
Class<?>[] basePackageClasses() default {};
다시 @ComponentScan
안에는 basePackages()가 있는데, String을 받는다.
Type-safe를 위해 아래 basePackageClasses()를 통해, 입력 클래스 기준으로 scan을 시작한다.
=> 외부 package는 스캔이 되지 않고, Bean으로 등록이 안된다.
@Filter도 중요하다.
해당하는 클래스는 스캔하지 않게 해준다!
이렇게 스프링 실행 시, 빈을 스캔해서 등록해준다.
실제 스캐닝은 ConfigurationClassPostProcessor 라는 BeanFactoryPostProcessor에 의해 처리된다.
단점: 초기 구동에서 모두 등록하기 때문에 (싱글톤 스코프), 최초 스프링 구동시에 시간이 걸린다.
(프록시? 등을 사용하기 때문이라는데, 정확한 이유는 찾아보자)
그래서 functional하게 빈을 직접 코드로 등록하는 방법도 가능하다. (스프링 부트는 이렇게 하는듯?)
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(Demospring51Application.class)
.initializers((ApplicationContextInitializer<GenericApplicationContext>)
applicationContext -> {
}
이렇게 initializers를 이용해 직접 Bean 등록이 가능하다.
이를 통해 조건문(if)등을 추가하여 빈을 등록하는 방식을 구현할 수 있다.
정리:
@ComponentScan은 스캔 범위, 스캔 필터를 지정한다.
@Component(Controller, Service etc)를 스캔하고 빈으로 등록한다.
Functional한 방식의 빈 등록도 가능은 하다! (Component Scan을 대체하는건 비추!!)