- study
- Spring version 5.x
- Boot 2.2
- Open JDK 11
- Junit5 + AssertJ + Mockito
- 1주차 미션 : 네이버 Open API 연동 및 컨텐츠 API 구현
- 2주차 미션 : 2주차 미션
- @SpringBootConfiguration
- @ComponentScan
- @EnableAutoConfiguration
@SpringBootApplication 어노테이션은 위 3가지의 어노테이션을 사용하는것과 같다.
-
어플리케이션 등록시, 빈을 두 단계를 거쳐 등록 한다.
- @ComponentScan
- @EnableAutoConfiguration
-
먼저 @ComponentScan으로 빈이 등록이 되는데, @SpringBootApplication 내부에 @ComponentScan을 살펴보면 filter로 AutoConfiguration을 통해 등록되는 빈들을 제외하라고 설정 되어 있다.
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class} // <-
)}
)
- @ComponentScan으로 빈이 등록 된 다음 @EnableAutoConfiguration이 추가적인 빈들을 등록 한다. (ex ServletWebServerFactory )
- @EnableAutoConfiguration은 추가된 jar dependency 기반으로 Spring application을 자동으로 설정하는 것을 시도한다
- @EnableAutoConfiguration이 빈을 등록하는 방법은
org.springframework.boot:spring-boot-autoconfigure Library 안의 META-INF/spring.factories 라는 파일에 있는 값들을 읽어 등록 한다.
spring.factories 파일 안에 키 밑에 설정되어있는 value(클래스들)들은 다 적용이 된다.
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ .... ....
-
파일 내용을 보면 다 클래스로 명시 되어 있고 각 클래스 내부에는 @Configuration 어노테이션을 통해 빈 설정파일로 등록 된다.
다만 @ConditionalOn.... 어노테이션에 맞는 조건에 따라 등록되거나 등록이 안되기도 한다.
ex) WebMvcAutoConfiguration.class- @ConditionalOnWebApplication(type = Type.SERVLET) : 웹 어플리케이션 타입이 servlet일 때만 AutoConfigure
- @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
: 클래스패스에 Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class가 있을 때 AutoConfigure
@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 {
...
}
-
만약 @ComponentScan과 @EnableAutoConfigure가 같은 빈을 등록한다면 @ComponentScan 때 등록한 빈을 @EnableAutoConfigure가 덮어 씌울 것이다.
덮어쓰는 걸 방지하려면 @ConditionalOnMissingBean를 통해 빈을 등록 못했을때 등록하도록 설정해준다.
- 스프링 부트는 내장 서블릿 컨테이너를 쉽게 사용할수 있게, 스프링 프레임워크를 쉽게 사용할 수 있게하는 Tool이다.
- 내장 서블릿 컨테이너도 자동설정의 일부분이다.
- 자바로 내장 웹서버인 톰캣 객체를 생성할 수 있다.
Tomcat tomcat = new Tomcat(); // 톰캣 객체 생성
tomcat.setPort(8081); // 포트 설정
Context context = tomcat.addContext("/", "/"); // 톰캣에 컨텍스트 추가
// 서블릿 만들기
HttpServlet servlet = new HttpServlet() {
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<head>");
writer.println(" <title>mission1</title>");
writer.println("</head>");
writer.println("<body>hello world</body>");
writer.println("</html>");
}
};
String servletName = "apiServlet";
tomcat.addServlet("/", servletName, servlet); // 톰캣에 서블릿 추가
context.addServletMappingDecoded("/api", servletName); // 컨텍스트에 서블릿 매핑
tomcat.getConnector();
tomcat.start(); // 톰캣 실행
tomcat.getServer().await(); // 톰캣 요청 대기
- 다만 별도의 서블릿들을 다 정의해야 되기 때문에 위 코드 과정을 자동 설정하고 실행하게 해주는 것이 스프링 부트의 기능 중 하나이다.
- 그렇다면 위 소스코드 처럼 WAS 설정은 어디에 있고 어떤 원리로 스프링 부트가 실행할 수 있는건가?
- 위에서 배웠던 @AutoConfiguration 를 통해 자동 설정이 된다.
- Library org.springframework.boot:spring-boot-autoconfigure/META-INF/spring.factories
- ServletWebServerFactoryAutoConfiguration.class -> 서블릿 웹서버 생성
- ServletWebServerFactoryConfiguration
- TomcatServletWebServerFactory.getWebServer() -> 톰캣 설정
- TomcatServletWebServerFactoryCustomizer -> 톰캣 커스터마이징
- DispatcherServletAutoConfigure.class -> 서블릿 만들고 등록
- 내부에서 HttpServlet 을 상속받은 DispatcherServlet 을 생성하고 서블릿 컨테이너에 등록
- ServletWebServerFactoryAutoConfiguration.class -> 서블릿 웹서버 생성
- 서블릿 컨테이너와 디스패처 서블릿 등록이 별도로 분리되어 있는 이유는
- 서블릿 컨테이너는 설정에 따라 달라질 수 있지만, 서블릿은 변하지 않기에
- 디스패처 서블릿이 어떤 서블릿 컨테이너를 사용하든 상관 없이 서블릿을 등록할 수 있도록 분리
- 스프링 부트가 실행되면 자동으로 톰캣 객체를 생성하고, 내장 서블릿 컨테이너(톰캣)에 서블릿이 추가가 되고 Web MVC 설정이 되면서 어플리케이션이 동작한다.
- 성능이슈를 위해 톰캣보단 언더토우를 사용하자. https://okky.kr/article/552112
- 공식 문서를 참고하여 언더토우로 변경해보자. Reference
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-undertow'
}
- 만약 내 어플리케이션을 웹 어플리케이션으로 만들기 싫은데 의존성에 웹관련 의존성 모듈이 있다면 스프링 부트는 자동으로 웹 어플리케이션으로 만들려고 한다.
- 프로퍼티 설정을 통해 클래스패스에 웹 관련 의존성이 있다 하더라도, 웹 어플리케이션으로 안바꿀 수 있다.
- spring.main.web-application-type=none
- 프로퍼티 설정을 통해 포트 변경 및 랜덤 포트로 변경할 수 있다.
- server.port = 8081
- server.port = 0 // 랜덤 포트
- ApplicationListener<ServletWebServerInitializedEvent>를 통해 현재 포트를 얻어올 수 있다.