Post

Spring - @SpringbootApplication가 뭘까 2편 EnableAutoConfiguration

개요

저번 포스팅에서는 메타 어노테이션들을 봤다. 이번에는 @EnableAutoConfiguration@ComponentScane 속성값들을 파볼것이다.

@EnableAutoConfiguration

이 또한 Javadoc를 읽어보면 친절하게 알려준다.

간단하게 이 어노테이션은 Bean을 등록하는 설정파일이고, spring.factories 내부에 따라 여러 Configure들이 있고 이를 조건에따라 등록하는 것이다.

@SpringBootApplication안에 있는 이 어노테이션이 결국 실행될 때 spring.factories에 있는 수많은 설정들이 자동으로 조건에 따라 적용되어 Bean이 생기는 것이다.

해당 jar파일을 보면 spring.factories가 있는걸 볼 수 있다. 기본적인 class-path에 저장된 값들도 등록한다.

예제는 이블로그에 잘 나와있다.

또한 이를 확인하는 방법은 간단한데, @EnableAutoConfiguration어노테이션에 지정되어있는 @Import(AutoConfigurationImportSelector.class)의 AutoConfigurationImportSelector는 DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered를 구현하게 되어있는데 이것들은 이름에서도 알 수 있게 특정 조건에 따라서 빈들을 담아온다.

그래서 이 들 중 하나에 들어가서 디버그를 찍으면 빈들이 등록되는것을 볼 수 있다.

1
2
3
@EnableAutoConfiguration
    -> @AutoConfigurationPackage
        -> @Import(AutoConfigurationPackages.Registrar.class) 에서 Registrar

를 타고 들어가면 다음과 같이 구현 되어있는걸 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    }

    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new PackageImports(metadata));
    }

}

여기서 register()의 인자를 설명해보면, registry는 빈 정의에 대해 관리를 하고, 두번째 인자로 넘겨주는 것은 이 어플리케이션에 있는 패키지 이름들이다.

디버그를 찍고 확인해보면

beanDefinitionNames에 내 프로젝트에 등록된 빈 이름들을 볼 수 있다.

이는 근데 어떻게 미리 알고 있을까? 이는 뒤에나올 ComponentScan을 먼저 수행하기 때문이다.

아무튼 빈에 대한 정보들을 가지고 있고 new PackageImports~이부분은 내 프로젝트 같은 경우 이렇게 이루어져있다.

실제 디버그를 또 직고 구현과정을 보면 basePackageClasses이라는 것과 basePackages라는 것을 넣고있는데,

이 두개는 다음과 같이 활용되기도 한다.

1
2
@ComponentScan(basePackageClasses = Application.class)
@ComponentScan(basePackages = com.io.realworld)

별 다른 조건이 없으면 @SpringBootApplication가 정의된 곳이 basePackage가 되는것이다.

이 어노테이션은 @Import(AutoConfigurationImportSelector.class)라는 구문도 가지고 있는데

위에서 설명한 META-INF/spring.factories에 있는 자동설정해야할 클래스들의 이름 목록을 반환한다. 이 또한 디버그를 찍고 보면

내가 안 쓴 Configuration이 있는거 보니 기본적인 Configuration은 다 가져온다.

ComponentScan

컴포넌트 스캔은 @Configuration, @Controller, @Service, 등 다른 @Component가 붙은 클래스들을 스캔하고 빈에 등록한다.

위에서 basePackageClasses, basePackages에 대해서 말했는데 이를 따로 설정해주지 않으면 디폴트 설정 패키지를 스캔한다.

1
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)

구문을 해석하자면 Custom타입의 필터를 등록하는데, TypeExcludeFilter 클래스에 정의된 필터로 가져온다는 뜻이다.

그러면 TypeExcludeFilter에 있는 match()메소드를 봐야한다. 왜냐하면 TypeFilter를 상속받고 있는데, 이 메소드를 오버라이딩 해줘야하기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
        throws IOException {
    if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
        for (TypeExcludeFilter delegate : getDelegates()) {
            if (delegate.match(metadataReader, metadataReaderFactory)) {
                return true;
            }
        }
    }
    return false;
}

TypeExcludeFilter클래스에 있는 메소드다. 무슨의미인지 해석하다가 날밤 샐거 같아서 나중에 기회되면 알아보는게 좋을것 같다.

또한

1
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

이것도 적용되어있는데 비슷한 방식으로

1
2
3
4
5
6
//AutoConfigurationExcludeFilter.class
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
        throws IOException {
    return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}

로 되어있다.

결론

@SpringBootApplication

  • @EnableAutoConfiguration를 통해 설정들을 자동으로 해주고
  • @ComponentScan을 통해 각 필터에 맞는 어노테이션을 스캔해준다.

더 깊이 파고 싶지만 머리가 복잡해져간다. 점점 산으로 가는 느낌도 들고 일단 목적에 맞게 @SpringBootApplication 어노테이션이 어떤식으로 동작되는지 간단하게 알아봤다.

Reference

This post is licensed under CC BY 4.0 by the author.