WAS 서버 실행시점에 필요한 초기화 작업들이 있다.
- 스프링 컨테이너에 들어오기 전단계의 초기화 (서블릿 초기화, 필터 초기화)
- 스프링 컨테이너 초기화
- 서블릿과 스프링 컨테이너의 연결
스프링 부트는 이 단계의 초기화 작업을 자동으로 제공하지만 그 구조를 알아보기 위해 학습해 보았다.
@WebServlet 어노테이션을 통한 서블릿 초기화도 가능하다. 이전 블로그 포스팅에 해당 내용을 작성한 적이 있다.
https://programmingjun.tistory.com/54
Spring MVC 구조 뜯어보기(1) - Servlet
서블릿 스프링 부트는 톰캣 서버를 내장하고 있어, 톰캣 서버 설치 없이 서블릿 코드를 사용 가능하다. 디스패쳐 서블릿은 웹 애플리케이션 서버를 통해 특정 URL에 요청이 오면 요청값인 HttpServl
programmingjun.tistory.com
이번 포스팅에서는 자바 코드를 통한 서블릿 초기화와 애플리케이션 초기화를 적어보고자 한다.
스프링은 ServletContainerInitializer라는 인터페이스로 서블릿 컨테이너의 초기화를 지원한다.
서블릿 컨테이너 초기화 코드
public class MyContainerInitV1 implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("MyContainerInitV1.onStartup");
System.out.println("set = " + set);
System.out.println("servletContext = " + servletContext);
}
}
추가적으로
resources/META-INF/services/jakarta.servlet.ServletContainerInitializer
경로에 WAS 가 실행 시 초기화할 클래스의 정보를 입력해주어야 한다.
컨테이너 초기화 시 print 한 로그를 확인해 보면 아래와 같다.
MyContainerInitV1.onStartup
set = null
servletContext = org.apache.catalina.core.ApplicationContextFacade@1d200c9d
8080 포트의 "/hello-servlet" 경로로 접근했을 때의 서블릿을 초기화하기 위해선 아래와 같이 구현할 수 있다.
컨택스트를 초기화하는 인터페이스
public interface AppInit {
void onStartup(ServletContext context);
}
서블릿을 컨택스트에 저장하는 과정
1. 직접 생성한 서블릿을 매핑
public class AppInitV1Servlet implements AppInit {
@Override
public void onStartup(ServletContext context) {
System.out.println("AppInitV1Servlet.onStartup");
ServletRegistration.Dynamic helloServlet = context.addServlet("helloServlet", new HelloServlet());
helloServlet.addMapping("/hello-servlet");
}
}
2. spring에서 제공하는 dispatcherServlet 매핑 (HelloConfig 내부에는 Bean의 설정값이 들어있다.)
public class AppInitV2Spring implements AppInit{
@Override
public void onStartup(ServletContext context) {
System.out.println("AppInitV2Spring.onStartup");
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(HelloConfig.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
ServletRegistration.Dynamic dispatcherServletV2 = context.addServlet("dispatcherServletV2", dispatcherServlet);
dispatcherServletV2.addMapping("/spring/*");
}
}
저장된 컨택스트의 정보로 애플리케이션을 초기화 하는 과정
@HandlesTypes(AppInit.class)
public class MyContainerInitV2 implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
System.out.println("MyContainerInitV2.onStartup");
System.out.println("MyContainerInitV2 set = " + set);
System.out.println("MyContainerInitV2 servletContext = " + servletContext);
for (Class<?> aClass : set) {
try {
AppInit appInit = (AppInit) aClass.getDeclaredConstructor().newInstance();
appInit.onStartup(servletContext);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
- @HandlesTypes에 초기화 인터페이스를 지정한다.
- 서블릿 컨테이너 초기화( ServletContainerInitializer )는 파라미터로 넘어오는 클래스 정보가 담긴 Set에 애플리케이션 초기화 인터페이스의 구현체들을 모두 찾아서 클래스 정보로 전달한다. 이때 AppInit.class를 구현한 AppInitV1Servlet.class 정보가 전달된다.
- 동적으로 생성자를 호출하는 메서드인 appInitClass.getDeclaredConstructor().newInstance() 을 사용해 리플렉션으로 객체를 생성한다.
- appInit.onStartup(ctx) 애플리케이션 초기화 코드를 실행하면서 서블릿 컨테이너 정보가 담긴 ctx 도 함께 전달한다.
해당 컨테이너 초기화 클래스의 정보도 resources/META-INF/services/jakarta.servlet.ServletContainerInitializer 경로에 저장한다.
실행 로그
MyContainerInitV1.onStartup
set = null
servletContext = org.apache.catalina.core.ApplicationContextFacade@1d200c9d
MyContainerInitV2.onStartup
MyContainerInitV2 set = [class hello.container.AppInitV2Spring, class hello.container.AppInitV1Servlet]
MyContainerInitV2 servletContext = org.apache.catalina.core.ApplicationContextFacade@1d200c9d
AppInitV2Spring.onStartup
AppInitV1Servlet.onStartup
위 구조는 스프링 MVC의 Intializer와 동일한 구조를 가지고 있으며 MVC 라이브러리에서 제공하는 WebApplicationInitializer의 내부는 아래와 같다.
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//초기화 코드 생략
}
'Spring' 카테고리의 다른 글
Spring boot 의 외부 설정 (1) | 2024.04.27 |
---|---|
Spring boot 의 내장 톰캣 (0) | 2024.04.21 |
공공데이터 Springboot로 파싱 (0) | 2024.04.13 |
Security CORS 설정 (0) | 2024.03.17 |
Security 인가(Authorization) 적용 (1) | 2024.03.16 |