Spring Boot를 살펴보자 1 . @EnableAutoConfiguration

jiho·2021년 6월 16일
2

Spring

목록 보기
11/13

요즘은 Spring Boot 기반으로 대부분 프로젝트를 진행할 테니 Spring Boot를 한번 정리해보겠습니다.

Spring Boot 소개

Spring Boot helps you to create stand-alone, production-grade Spring-based applications that you can run. We take an opinionated view of the Spring platform and third-party libraries, so that you can get started with minimum fuss. Most Spring Boot applications need very little Spring configuration.
You can use Spring Boot to create Java applications that can be started by using java -jar or more traditional war deployments. We also provide a command line tool that runs “spring scripts”.

공식 문서에서는 스프링 부트를 Stand-Alone로 동작하며 Production 수준의 Spring 기반의 어플리케이션을 생성하는 것을 도와준다고 합니다.

StandAlone이라는 의미는 Servlet Container인 외부 톰캣 서버에 배포하는 방식이 아니라 내장 Tomcat을 가지고 DispatcherServlet를 을 내장 Tomcat에 올리기 때문에 어플리케이션처럼 Jar 형태로 실행 할 수 있기 때문입니다.

Spring Boot 프로젝트 생성 및 설정

Spring Boot 프로젝트를 생성하려면 https://start.spring.io/ 를 통해 원하는 초기 설정을 할 수도 있고 직접 Maven이나 Gradle으로 구성을 할 수도 있습니다. 직접 구성하는 방법은 공식 Reference로 대체하겠습니다.

Spring Boot 설정 공식 문서

@SpringBootApplication

프로젝트의 Entry Point를 살펴보겠습니다.

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

Main 문은 간단히 위와 같이 @SpringBootApplication 어노테이션을 적용하고 SpringApplication의 run Static Method를 호출하기만하면 서버가 실행됩니다.

@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 {
...
}

주목할 점은 @SpringBootConfiguration, @ComponentScan@EnableAutoConfiguration입니다.

@SpringBootConfiguration 어노테이션의 내부는 단순히 @Configration 어노테이션이 적용된 빈임을 나타내는 어노테이션입니다.

@ComponentScan 어노테이션은 프로젝트 내의 (어노테이션이 적용된 클래스가 포함된 패키지를 기준으로 이하의 패키지들을 탐색해서) Bean을 탐색해서 IoC Container에 탐색된 Bean을 등록합니다.

@EnableAutoConfiguration는 스프링 부트에서 필요한 자동설정에 관한 설정을 해줍니다. 여기서 중요한 점은 ComponentScan을 먼저 진행한 후에 자동설정(Auto Configuration)을 한다는 점을 잘 기억해야합니다.

위 3가지 구성이 @SpringBootApplication 어노테이션을 나타냅니다.

그렇다면 이번에는 @EnableAutoConfigration가 스프링 부트의 설정들을 구성해주는지 원리를 알아보겠습니다.

@EnableAutoConfiguration

위 어노테이션이 각 패키지들을 순회해서 META-INF속에 spring.facotries라는 파일 속에 org.springframework.boot.autoconfigure.EnableAutoConfiguration 키 값에 있는 설정을 실행 시켜줍니다.

이전에 한번 정리했던 SPI Service Provider Interface 패턴과 유사한 형태 입니다. 간단히 소개하면 SPI패턴도 자바에서 다른 기능을 유연하게 추가 확장할 수 있도록 각 패키지 속 META-INF 속에 확장될 SPI를 추가해서 특별한 코드 추가없이 기능을 확장하는 방식을 말합니다. SPI 정리 글

@EnableAutoConfiguration도 마찬가지로 새로운 빈 설정들을 패키지를 추가하는 것만으로 쉽게 확장 설정하는 방식을 말합니다.

Spring Boot에서는 default로 어떤 설정들을 해주는지 spring.facotries 파일을 살펴보겠습니다.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
...
rg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
...

너무 많은 값들이 있어서 이번에 소개할 몇가지 자동설정들에 대해서만 보도록 하겠습니다.

익숙한 이름인 WebMvcAutoConfigurationSpring WebMVC와 관련된 특별한 Bean들을 설정해주는 설정 코드인 것 같습니다.

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration { ... }

직접 확인해보면 @Configuration 어노테이션을 적용해서 빈들을 등록하는 코드임을 확인할 수 있습니다.

조건적으로 Bean 등록하는 @Configuration가 적용된 클래스들

spring.facotories meta 파일 속에 자바 설정들은 항상 모든 빈들을 등록하지는 않습니다. 아래 예시를 보면 알겠지만 조건적으로 각 빈들을 등록하는 모습들을 살펴볼 수 있습니다.

@Bean
@ConditionalOnMissingBean({FormContentFilter.class})
@ConditionalOnProperty(
    prefix = "spring.mvc.formcontent.filter",
    name = {"enabled"},
    matchIfMissing = true
)
public OrderedFormContentFilter formContentFilter() {
    return new OrderedFormContentFilter();
}

@ConditionalOn...와 같은 식의 어노테이션들을 이용해서 Bean들이 미리 등록되 있으면 Bean을 추가하지않는 방식으로 설정 코드들을 상황에 따라 조건적으로 등록할 수 있습니다. 이렇게 하면 재정의해서 다른 Bean을 사용하고 싶을 경우, 커스텀하게 추가한 Bean을 우선적으로 추가하는 방식을 취할 수 있습니다. 실습을 해보면서 이 부분을 확인해보겠습니다.

외부 프로젝트로 라이브러리 작성 후 스프링 자동 설정으로 등록해보기

우선 메이븐 프로젝트로 외부 프로젝트를 생성합니다.

  1. 스프링 빈 자동설정을 위해 필요한 의존성들을 추가해보겠습니다.
  <dependencies>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-autoconfigure</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-autoconfigure-processor</artifactId>
          <optional>true</optional>
      </dependency>
  </dependencies>

  <dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-dependencies</artifactId>
              <version>2.0.3.RELEASE</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
  </dependencyManagement>
  1. 빈으로 제공할 클래스와 빈 설정을 위한 클래스를 작성하겠습니다.
public class ExampleService {
	...
}
@Configuration
public class ExampleConfiguration {

	@Bean
    public ExampleService exampleService() {
    	return new ExampleService();
    }
}
  1. META-INF/spring.factories 파일에 위 Configuration 파일의 풀 패키지 이름을 등록하겠습니다.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    me.test.ExampleServiceConfiguration\

위와 같이 org.springframework.boot.autoconfigure.EnableAutoConfiguration을 키로 해당 클래스 full path name을 등록하게 되면 @EnableAutoConfiguration이 적용된 다른 스프링 프로젝트에서 해당 프로젝트의 의존성을 추가했을 때 ExampleConfiguration 설정이 등록되게 됩니다.

그리고 maven plugin 중 install을 사용하면 로컬 maven repository에 설치가 되게되고 다른 프로젝트에서 해당 배포된 프로젝트를 의존성에 추가할 수 있게 됩니다.

spring-boot-autoconfigure-processor

의존성으로 등록했던 spring-boot-autoconfigure-processor의 역할이 궁금해서 찾아봤는데 annotation processor이며 컴파일 단계에서 spring-autoconfigure-metadata.properties를 생성하는 걸 확인할 수 있습니다. Condition 정보를 담고 있는 걸로 보아 Bean Configuration이 실행될 때 사용되는 데이터로 보입니다.

조건적으로 Bean 등록하게 하려면?

만약 의존성으로 추가된 프로젝트가 추가하는 Bean이 아니라 개발자가 동일한 타입의 커스텀 빈을 등록하고 싶다면 아마 따로 @Configuration을 적용한 클래스에서 @Bean 어노테이션을 적용해 등록할 것입니다.

@Configuration
public class WebConfig { 
	@Bean
    public ExampleService exampleService {
    	return new CustomExampleService();
    }
}

하지만 이대로 등록하게되면 다른 프로젝트에서 정의했던 ExampleSerivce가 여전히 등록되어있을 것입니다. 이러한 이유는 Component Scan을 수행하고 AutoConfiguration을 수행하기 때문에 입니다.

이를 해결하려면 외부에서 자동설정으로 등록될 Bean에 특별한 어노테이션(@ConditionalOnMissingBean)을 추가해주면 됩니다.

@Configuration
public class ExampleConfiguration {

	@Bean
    @ConditionalOnMissingBean
    public ExampleService exampleService() {
    	return new ExampleService();
    }
}

ExampleService 타입의 Bean이 이미 등록되어있다면 무시됩니다.

profile
Scratch, Under the hood, Initial version analysis

0개의 댓글