Spring boot 자동 설정
Spring boot로 와서 가장 크게 달라진 점은 설정 부분이다.
@SpringBootApplication
하나만 추가하면 필요한 모든 설정을 알아서 추가한다.
이 자동 설정 기능만을 위해 autoconfigure라는 모듈이 작성되어 있고 그 덕분에 Spring boot의 핵심인 @SpringBootApplication
을 사용할 수 있다.
가시적으로 새로운 기능은 아니지만 편리성을 비약적으로 상승시켜 생산성을 어마어마하게 높여주었다.
@SpringBootApplication
Spring boot 프로젝트를 생성하면 main 메소드가 포함된 클래스에 자동으로 추가되는 어노테이션이다.
어노테이션의 정의를 타고 올라가면 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 {
@AliasFor(annotation = EnableAutoConfiguration.class)
Class<?>[] exclude() default {};
@AliasFor(annotation = EnableAutoConfiguration.class)
String[] excludeName() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
7가지 어노테이션이 달려있지만 스프링 고유 어노테이션은 아래 3개 뿐이다.
하나씩 따라가본다.
@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration { }
@Configuration
하나를 제외하면 모두 자바 기본 어노테이션이다.
@Configuration
어노테이션이 붙어 있으니 메인 클래스도 Bean으로 등록된다.
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
// main 메소드를 포함하는 클래스
private final DemoApplication demoApplication;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(demoApplication);
// com.example.demo.DemoApplication$$EnhancerBySpringCGLIB$$17e91336@236ab296 출력
}
}
@EnableAutoConfiguration
이 어노테이션에는 2가지 하위 어노테이션이 포함된다.
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
먼저 살펴본다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage { }
AutoConfigurationPackages.Registrar.class
의 정의로 이동한다.
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
Registrar
는 ImportBeanDefinitionRegistrar
의 구현체이다.
bean 자동 등록 과정에서는 registerBeanDefinitions 메소드를 사용하고 있으며, 두 개의 매개변수를 받는다.
AnnotationMetadata
metadata- 어노테이션이 부착된 클래스의 이름, 어노테이션의 데이터 등을 담고 있다.
BeanDefinitionRegistry
registry- bean 라이프사이클에서 생성하는 beanFactory를
BeanDefinitionRegistry
타입으로 변경한 것이다. - 생성부 :
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
- registerBeanDefinitions 메소드의 매개변수에 있는
BeanDefinitionRegistry
타입의 인터페이스는 bean 정의를 관리하는 역할을 맡고 있다. BeanDefinitionRegistry
에 관한 내용은 이전에 작성한 포스트(bean 생성)에서 확인할 수 있다.
- bean 라이프사이클에서 생성하는 beanFactory를
register(...)에 브레이크 포인트를 찍고 다음으로 몇 번 넘어가면 ConfigurationClassBeanDefinitionReader
클래스의 loadBeanDefinitions 메소드에서 루프를 돌고 있는 모습이 보인다.
forEach 내부의 ConfigurationClass
configClass가 autoconfigure에 등록된 자동 설정 정보를 갖고 있으며, 하나씩 돌면서 등록하는 것이다.
@Import(AutoConfigurationImportSelector.class)
를 확인해 볼 차례다.
AutoConfigurationImportSelector
의 정의를 타고 올라가보자.
내려가다 보면 getCandidateConfigurations라는 메소드가 있다.
이 메소드는 자동 설정을 하고자 하는 후보군 클래스의 이름 목록을 반환한다.
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
Assert의 메시지를 보면 META-INF/spring.factories에 클래스 정보가 있어야 함을 알 수 있다.
spring.factories의 위치는 spring boot의 autoconfigure 모듈 아래에 존재한다.
이 파일을 열어보면 # Auto Configure 주석 아래에 자동으로 컨픽을 등록할 클래스 목록이 나열되어 있다.
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
... # 이하 생략
@ComponentScan
@ComponentScan
은 스프링을 공부해보았다면 이미 잘 아는 어노테이션이다.
특정 위치의 @Component
를 전부 읽어 bean으로 등록하는 역할을 담당한다.
단, excludeFilters 규칙이 걸려 있기 때문에 spring boot에 의해 자동 등록된 bean은 제외한다.
이 어노테이션의 존재로 인해 메인 클래스의 위치가 굉장히 중요하다.
┌─ root
└─┬─ com
└─┬─ example
└─┬─ demo
├── controler
├── service
├── dto
└── DemoApplication.java
보통 이 경우 demo 아래에 DemoApplication.java가 메인 클래스로 존재하는데,
demo 패키지 이하는 사실상 프로젝트 전체를 포함한다.
만약 메인 클래스가 Dto 클래스에 위치한다면 controller나 service 패키지의 bean은 읽지 못한다.
'Java > Spring framework' 카테고리의 다른 글
Spring에서 AOP를 구현하는 방법과 Transactional (0) | 2020.05.26 |
---|---|
Spring(boot)에서 초기화 코드를 작성하는 방법 (0) | 2020.05.11 |
Spring framework 소스코드 읽어보기 - Bean 생성 원리 (3) (0) | 2020.02.23 |
Spring framework 소스 코드 읽어보기 - Bean 생성 원리 (2) (1) | 2020.01.27 |
Spring framework core (12) - Spring AOP (0) | 2020.01.24 |