- web.xml과 ServletContainerInitializer
- web.xml : 서블릿 컨테이너가 웹 애플리케이션을 초기화할 때 필요한 정보를 제공한다.
- ServletContainerInitializer : 서블릿 컨테이너가 시작될 때 ServletContainerInitializer를 구현한 클래스를 실행하여 웹 애플리케이션을 초기화한다.
- ServletContext는 서블릿 컨테이너라고 볼 수 있다. 따라서 web.xml이나 ServletContainerInitializer의 핵심은 결국 ServletContext 객체를 초기화하기 위함이다.
ServletContainerInitializer
public interface ServletContainerInitializer
{
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
- Servlet 3.0부터 ServletContainerInitializer의 등장으로 기존의 web.xml 기반 대신 코드 기반으로 서블릿 컨테이너를 초기화하는 작업(ServletContext 객체 초기화)이 가능해졌다.
- ServletContainerInitializer 인터페이스를 구현한 클래스가 있으면, ServletContainerInitializer를 구현한 클래스는 서블릿 컨테이너가 시작될 때 로드되고 인스턴스화되며 onStartup 메소드가 호출된다.
- ServletContainerInitializer 인터페이스를 구현한 클래스가 있으면, ServletContainerInitializer를 구현한 클래스는 서블릿 컨테이너가 시작될 때 로드되고 인스턴스화되며 onStartup 메소드가 호출된다.
- ServletContainerInitializer 구현
- ServletContainerInitializer 인터페이스를 구현하는 클래스를 만든 후, /META-INF/services/jakarta.servlet.ServletContainerInitializer 파일을 생성하고 ServletContainerInitializer 인터페이스를 구현한 클래스의 이름을 해당 파일에 적어준다.
- ServletContainerInitializer 인터페이스를 구현하는 클래스에 @HandlesTypes 어노테이션으로 클래스를 지정해주면, 서블릿 컨테이너가 시작될 때 지정된 클래스를 찾아서 ServletContainerInitializer 인터페이스를 구현하는 클래스의 onStartup() 메소드에 지정된 클래스와 ServletContext 객체를 인자로 넣어준다.
ServletContext 객체 초기화
- 서블릿 컨테이너가 시작될 때 수행되는 웹 애플리케이션을 초기화하는 작업인 “ServletContext 객체 초기화”에 대해서 web.xml 방식과 ServletContainerInitializer 방식의 예시를 살펴보자.
- web.xml 방식
- 서블릿 컨테이너가 시작되고 웹 애플리케이션이 초기화될 때, 서블릿 컨테이너는 web.xml을 읽은 후 ServletContext 객체를 초기화한다.
- ServletContainerInitializer 방식
- 서블릿 컨테이너가 시작되고 웹 애플리케이션이 초기화될 때, 서블릿 컨테이너는 ServletContainerInitializer 인터페이스를 구현한 클래스를 찾은 후 해당 클래스를 로딩하고 인스턴스화한다. 이후 해당 클래스의 onStartup() 메소드를 호출하여 ServletContext 객체를 초기화한다.
ServletContext 객체 초기화 1 - ServletContext 초기화 파라미터 설정, 서블릿 등록 등
- web.xml 방식
- 웹 애플리케이션 구조
/src /main /java /com /example ExampleServlet.java /webapp /WEB-INF web.xml - 서블릿 클래스 생성
public class ExampleServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { System.out.println("service 메소드 호출"); } } - web.xml 작성
<!-- ServletContext 초기화 파라미터 설정 --> <context-param> <param-name>contextParamName_example</param-name> <param-value>contextParamValue_example</param-value> </context-param> <!-- 서블릿 등록 --> <servlet> <servlet-name>exampleServlet</servlet-name> <servlet-class>com.example.ExampleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>exampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping> <!-- 필터, 리스너 등 웹 애플리케이션 구성 요소 관련 --> ...
- 웹 애플리케이션 구조
- ServletContainerInitializer 방식
- 웹 애플리케이션 구조
/src /main /java /com /example ExampleServlet.java MyServletContainerInitializer.java MyAppInitializer.java MyAppInitializerV1.java /resources /META-INF /services jakarta.servlet.ServletContainerInitializer - 서블릿 클래스 생성
public class ExampleServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { System.out.println("service 메소드 호출"); } } - ServletContainerInitializer 구현 클래스 생성
@HandlesTypes(MyAppInitializer.class) public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException { for (Class<?> myAppInitClass : classes) { try { MyAppInitializer initializer = (MyAppInitializer) myAppInitClass.getDeclaredConstructor.newInstance(); initializer.onStartup(servletContext); } catch (Exception e) { throw new RuntimeException(e); } } } } - MyAppInitializer 인터페이스 작성 및 구현
public interface MyAppInitializer { void onStartUp(ServletContext servletContext); }public class MyAppInitializerV1 implements MyAppInitializer { @Override public void onStartUp(ServletContext servletContext) { // setInitParamater와 setAtturibute는 용도와 동작 방식 측면에서 여러 차이가 있음 // setInitParameter() // - 설정 후 변경 불가 -> 이미 해당 초기화 파라미터가 설정되있으면 실패함 // - 값은 문자열 형식 // - 설정은 주로 초기화 시에 한 번만 수행 // - 웹 애플리케이션의 전역 설정에 주로 사용 // - web.xml의 context-param 태그와 유사함 -> 읽기 전용 // setAttribute() // - 설정, 변경, 제거 가능 // - 값은 객체 형식 // - 런타임 동안 동적으로 관리 // - 애플리케이션 컴포넌트 간 데이터 공유 및 상태 저장에 주로 사용 // ServletContext 초기화 파라미터 설정 servletContext.setInitParameter("contextParamName_example", "contextParamValue_example"); // 서블릿 등록 ServletRegistration.Dynamic exampleServlet = servletContext.addServlet("exampleServlet", new ExampleServlet()); exampleServlet.addMapping("/example"); } } - /META-INF/services/jakarta.servlet.ServletContainerInitializer 파일 생성 후 아래 내용 작성
com.example.MyServletContainerInitializer
- 웹 애플리케이션 구조
- => ServletContainerInitializer, web.xml의 핵심은 결국 ServletContext 객체를 초기화하는 것
- ServletContainerInitializer 방식의 코드(MyAppInitializerV1.class)에서 서블릿을 등록하는 부분인 servletContext.addServlet()를 보면, ServletContext 객체에 서블릿을 등록하는 형태로 이루어진다. 즉, 서블릿을 ServletContext 객체에 등록하는 것이 서블릿 등록의 본질이라고 볼 수 있다.마찬가지로, ServletContext 초기화 파라미터 설정은 물론, 필터나 리스너 등록 등의 작업도 결국은 ServletContext 객체에 등록하는 작업이다.
- web.xml 방식에서도 서블릿, 필터, 리스너, ServletContext 초기화 파라미터 등의 설정을 작성해두면, 해당 설정들이 ServletContext 객체에 등록된다. 따라서, ServletContainerInitializer와 web.xml은 ServletContext 객체를 초기화하는 작업을 수행하는 방식이 다를 뿐, 결과적으로 두 방식 모두 ServletContext 객체를 초기화한다.
ServletContext 객체 초기화 2 - 서블릿에 대한 추가 설정
- web.xml 방식
- web.xml 작성
<servlet> <servlet-name>exampleServlet</servlet-name> <servlet-class>com.example.ExampleServlet</servlet-class> <!-- 서블릿 초기화 파라미터 설정 --> <init-param> <param-name>servletParamName_example</param-name> <param-value>servletParamValue_example</param-value> </init-param> <!-- 서블릿 초기화 시점 설정 --> <load-on-startup>1</load-on-startup> </servlet> <!-- 서블릿 매핑 --> <servlet-mapping> <servlet-name>exampleServlet</servlet-name> <url-pattern>/example</url-pattern> </servlet-mapping>
- web.xml 작성
- ServletContainerInitializer 방식
- MyAppInitializer 구현
public class MyAppInitializerV1 implements MyAppInitializer { @Override public void onStartUp(ServletContext servletContext) { ServletRegistration.Dynamic exampleServlet = servletContext.addServlet("exampleServlet", new ExampleServlet()); // 서블릿 초기화 파라미터 설정 exampleServlet.setInitParameter("servletParamName_example", "servletParamValue_example"); // 서블릿 초기화 시점 설정 exampleServlet.setLoadOnStartup(1); // 서블릿 매핑 exampleServlet.addMapping("/example"); } }
- MyAppInitializer 구현
- => 서블릿 초기화 파라미터 설정, 서블릿 매핑 등의 서블릿에 대한 추가 설정 작업도 ServletContext 객체를 초기화하는 작업의 일부로 간주
- ServletContainerInitializer 방식의 코드(MyAppInitializerV1.class)를 보면, ServletContext 객체에 서블릿을 등록할 때 addServlet() 메소드가 사용되고 이 메소드는 ServletRegistration.Dynamic 객체를 반환한다. 즉, ServletRegistration.Dynamic 객체는 ServletContext를 통해 반환되며, 반환된 ServletRegistration.Dynamic 객체를 통해 서블릿에 대한 추가 설정을 수행할 수 있다.
- ServletContext의 getServletRegistration() 메소드를 사용하여 등록된 서블릿의 정보를 가져올 수 있다.
3. @WebServlet 어노테이션 방식 - 서블릿 클래스 생성(@WebServlet)
@WebServlet(name = "exampleServlet", urlPatterns = "/example", initParams = {@WebInitParam(name = "servletParamName_example", value = "servletParamValue_example")}, loadOnStartup = 1) public class ExampleServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { System.out.println("service 메소드 호출"); } } - @WebServlet을 사용하면 web.xml이나 ServletContainerInitializer 없이 서블릿 등록 및 매핑, 서블릿 초기화 파라미터 설정 등 서블릿에 대한 설정을 할 수 있다.
- @WebServlet은 개별 서블릿에 대한 간편한 설정을 제공하지만, 리스너 설정이나 ServletContext 객체의 초기화 파라미터 설정 등 복잡한 설정이나 웹 애플리케이션 전체 설정이 필요한 경우 web.xml이나 ServletContainerInitializer가 필요하다.
ServletContextListener
public interface ServletContextListener extends EventListener
{
default void contextInitialized(ServletContextEvent sce)
{
// 웹 애플리케이션이 초기화(시작)될 때 호출
// 필터나 서블릿이 초기화되기 전에 호출됨
}
default void contextDestroyed(ServletContextEvent sce)
{
// 웹 애플리케이션이 종료될 때 호출
// 모든 필터와 서블릿이 파괴된 후에 호출됨
}
}
- ServletContextListener 인터페이스를 구현한 클래스는 웹 애플리케이션의 ServletContext가 변경될 때 알림을 받는다.
- ServletContextListener는 ServletContext의 시작 및 종료 이벤트를 처리할 수 있으며, 웹 애플리케이션의 초기화(시작) 및 종료 시에 특정 작업을 수행하기 위해 사용된다.
- ServletContextListener는 주로 웹 애플리케이션 초기화 및 종료 시 필요한 설정 작업을 수행한다. 데이터베이스 연결을 설정하거나, 로깅을 구성하는 등의 다양한 작업을 포함한다.
- ServletContextListener 예시
- ServletContextListener를 구현한 클래스는 web.xml 설정, @WebListener 어노테이션, ServletContext의 addListener() 메소드를 통해 등록한다.
- web.xml 방식
- ServletContextListener 인터페이스 구현 클래스 생성
public class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { // JDBC 드라이버 로드 Class.forName("com.mysql.cj.jdbc.Driver"); // 데이터베이스 연결 설정 String dbUrl = sce.getServletContext().getInitParameter("DB_URL"); Connection connection = DriverManager.getConnection(dbUrl, "username", "password"); // ServletContext에 연결 객체 저장 sce.getServletContext().setAttribute("DBConnection", connection); } @Override public void contextDestroyed(ServletContextEvent sce) { connection.close(); } } - web.xml 작성
<listener> <listener-class>com.example.MyServletContextListener</listener-class> </listener>
- ServletContextListener 인터페이스 구현 클래스 생성
- 어노테이션 방식
- ServletContextListener 인터페이스 구현 클래스 생성(@WebListener)
@WebListener public class MyServletContextListener implements ServletContextListener { // 위와 동일 }
- ServletContextListener 인터페이스 구현 클래스 생성(@WebListener)
- addListener() 방식 - ServletContainerInitializer
- ServletContextListener 인터페이스 구현 클래스 생성
public class MyServletContextListener implements ServletContextListener { // 위와 동일 } - MyAppInitializer 구현
public class MyAppInitializerV1 implements MyAppInitializer { @Override public void onStartUp(ServletContext servletContext) { // ServletContext를 통해 리스너 등록 servletContext.addListener(new MyServletContextListener()); } }
- ServletContextListener 인터페이스 구현 클래스 생성
ServletContainerInitializer와 ServletContextListener
- ServletContainerInitializer VS ServletContextListener
- 목적
- ServletContainerInitializer : 서블릿 및 필터 등록 및 매핑, 리스너 등록 등
- ServletContextListener : 데이터베이스 연결 설정 및 해제, 로그 설정 등
- 호출 시점
- ServletContainerInitializer : 서블릿 컨테이너 시작 시 호출
- ServletContextListener : 웹 애플리케이션 초기화(시작) 및 종료 시 호출
- 목적
- ServletContainerInitializer의 onStartUp 메소드와 ServletContextListener의 contextInitialized 메소드 호출 순서
- 서블릿 컨테이너가 시작되면 서블릿 컨테이너는 ServletContainerInitializer 구현 클래스의 onStartUp(ServletContext servletContext) 메소드를 호출
- onStartUp 메소드에 전달되는 ServletContext 객체는 기본적인 초기화가 완료된 상태이며, onStartUp 메소드를 통해 추가적인 초기화 작업(서블릿, 필터, 리스너 등록 등)을 수행한다.
- onStartUp 메소드의 실행이 끝난 후, 서블릿 컨테이너는 등록된 모든 ServletContextListener 구현 클래스의 contextInitialized 메소드를 호출
- 즉, ServletContextListener의 contextInitialized 메소드가 호출되는 시점은 ServletContainerInitializer나 web.xml을 통해 ServletContext 객체가 초기화된 직후이다.
- 서블릿 컨테이너가 시작되면 서블릿 컨테이너는 ServletContainerInitializer 구현 클래스의 onStartUp(ServletContext servletContext) 메소드를 호출
참고 자료
https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/ServletContainerInitializer.html https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/ServletContext.html https://tomcat.apache.org/tomcat-7.0-doc/servletapi/javax/servlet/ServletRegistration.Dynamic.html https://tomcat.apache.org/tomcat-8.5-doc/servletapi/javax/servlet/ServletContextListener.html https://docs.oracle.com/javaee%2F6%2Fapi%2F%2F/javax/servlet/ServletContextListener.html https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/SpringServletContainerInitializer.html https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/WebApplicationInitializer.html https://kimcoder.tistory.com/511 https://recordsoflife.tistory.com/490 https://escapefromcoding.tistory.com/174 https://offbyone.tistory.com/215 https://nhs0912.tistory.com/81 https://blog.naver.com/kitepc/221314687808 https://velog.io/@rolroralra/Chapter0.-Servlet-컨테이너-Spring-컨테이너 https://amy-it.tistory.com/86 https://erjuer.tistory.com/20 https://scshim.tistory.com/399