순수 자바 클래스와 어노테이션을 이용한 시동
0. 개요
Servlet 3.0 이상 버전부터는 web.xml 없이 서블릿 컨텍스트를 초기화할 수 있는 방법이 나왔습니다. 따라서 servlet, filter, listener 컴포넌트의 등록과 설정을 코드로 입력하여 사용 가능하고 web.xml 파일과 같이 사용할 수 도 있습니다.
그래서 ServletContext에 관련 메소드들이 추가되었습니다. 이 메소드들을 사용하기 위해서 ServletContainerInitializer의 onStartup 메소드를 적절하게 호출하면 됩니다. 코드로 설정하는 것의 장점은 최종 사용자나 어플리케이션이 DD(deployment descriptor, 일명 web.xml)를 배포하지 않고도 프레임워크의 web component들을 수동적으로 추가할 수 있다는 점입니다. Spring Framework는 SpringServletContainerInitializer를 통해 이 전략을 사용합니다.
WAS 가 시작될 때 필요한 설정들이 시작되는 곳이 배포 설명자(Deploy Descriptor)인 web.xml 파일입니다.
ServletContainInitializer
package javax.servlet;
...
public interface ServletContainerInitializer {
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException;
}
servlet 3.0 spec
- 서버 실행 시 ServletContainerInitializer service provider를 찾은뒤 @HandleTypes로 정의된 클래스를 실행
javax.servlet.ServletContainerInitializer 인터페이스를 구현하는 클래스를 만들고,
spring-web-{ RELEASE-VERSION }/META-INF/services/javax.servlet.ServletContainerInitializer 파일에 패키지를 포함한 클래스명을 저장. (스프링에서는 "org.springframework.web.SpringServletContainerInitializer" 를 저장하고 있다.)
META-INF/service 안에 제공하고자 하는 서비스명의 파일을 넣고, 내용으로 그것을 구현한 클래스를 넣어 서비스 제공(Service Provider)
SpringServletContainerInitializer
package org.springframework.web;
import ...
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
- @HandleTypes에 정의된 클래스 혹은 구체적으로 구현 된 하위 클래스들은 SpringServletContainerInitializer 클래스의 onStartup()메소드를 통해 Set<Class<?>> 안으로 들어온다.
- if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass))
- waiClass가 인터페이스가 아니고, 추상 클래스도 아닌 경우에만 WebApplicationInitializer를 생성한다.
- 파라미터로 받은 Set<Class<?>> 을 반복해서 WebApplicationInitializer로 생성(newInstance)해 initializers list에 담는다.
- initializers list를 정렬해 WebApplicationInitializer#onStartup 메소드를 실행한다.
ServletContainerInitializer와 @HandleTypes의 사용으로 IOC 패턴을 적용하는 것이 가능해진 것.
개발자는 프레임워크를 사용하기 위해서 jar 의존성과 POJO(어노테이션이나 인터페이스의 구현체)를 결합하기만 하면 끝입니다.
그러므로, 스프링프레임웍을 xml 없이 실행하기 위해서는 org.springframework.web.WebApplicationInitializer를 구현하는 클래스를 만들어주면 되는 것입니다.
요약
- Spring-MVC에서 서블릿 기반의 웹 애플리케이션 설정을 담당하는 구현체는 ServletContainerInitializer 인터페이스를 구현한 SpringServletContainerInitializer 클래스다.
- SpringServletContainerInitializer#onStartup 메소드는 WebApplicationInitializer 인터페이스를 상속한 (인터페이스, 추상 클래스가 아닌)일반 클래스를 생성한다.
- WebApplicationInitializer 인터페이스의 구현체는 AbstractContextLoaderInitializer, AbstractDispatcherServletInitializer, AbstractAnnotationConfigDispatcherServletInitializer 추상 클래스다.
- 웹 애플리케이션 설정을 위해 WebApplicationInitializer 인터페이스를 상속한 JavaConfig(WebConfig) 클래스를 만들어야 한다.
- SpringServletContainerInitializer#onStartup 메소드에서 생성되는 WebApplicationInitializer 인터페이스의 구현체는 우리가 만든 WebConfig 구현체로 생성된다.
Ref.
- 톰캣이 어떻게 Spring Framework을 찾아내고 초기화 하는지?
https://medium.com/chequer/tomcat-spring-bootstrapping-sequence-2%ED%8E%B8-spring-e19705529132
https://offbyone.tistory.com/215
https://akaroice.tistory.com/13
https://escapefromcoding.tistory.com/174
AbstractContextLoaderInitializer
https://blog.woniper.net/367?category=699184
AbstractDispatcherServletInitializer와 AbstractAnnotationConfigDispatcherServletInitializer