본문 바로가기
교재 실습/자바 웹 개발 워크북

65. ServletContextListener와 객체 공유 (2)

by Jint 2022. 7. 10.

2. 리스너 ServletContextListener 만들기

웹 애플리케이션의 시작과 종료 이벤트를 처리할 리스너는 ServletContextListener 인터페이스를 구현해야 한다. 다음 그림은 이번 실습에서 만들 리스너의 계층도이다(그림 1).

그림 1 (ServletContextListener 인터페이스의 구현체)

▶ contextInitialized() 메서드는 웹 애플리케이션이 시작될 때 호출된다. 공용 객체를 준비해야 한다면, 이 메서드에 작성하면 된다.

▶ contextDestroyed() 메서드는 웹 애플리케이션이 종료되기 전에 호출된다. 자원 해제를 해야 한다면, 이 메서드에 작성하면 된다.

클래스의 이름을 ContextLoaderListener로 정한 이유는, 나중에 스프링 프레임워크를 배울 때 좀 더 이해하기 쉽게 만들기 위함이다. 스프링 프레임워크에서도 같은 역할의 같은 이름을 가진 클래스가 존재한다.

 

web05 프로젝트에 spms.listeners 패키지를 만든다. 이후 이 패키지에 ContextLoaderListener 클래스를 생성한다(그림 2).

그림 2 (ContextLoaderListener 클래스 생성)

package spms.listeners;

import java.sql.Connection;
import java.sql.DriverManager;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
//import javax.servlet.annotation.WebListener;

import javax.servlet.annotation.WebListener;

import spms.dao.MemberDao;

//리스너의 배치 : @WebListener 어노테이션 사용
@WebListener
public class ContextLoaderListener implements ServletContextListener {
	Connection conn; //인스턴스 변수
  
	@Override
	public void contextInitialized(ServletContextEvent event) {
		try {
			//DB 커넥션 객체 준비
			ServletContext sc = event.getServletContext();
			Class.forName(sc.getInitParameter("driver"));
			conn = DriverManager.getConnection(
					sc.getInitParameter("url"),
					sc.getInitParameter("username"),
					sc.getInitParameter("password"));
			//MemberDao 객체 준비하여 ServletContext에 보관
			MemberDao memberDao = new MemberDao();
			memberDao.setConnection(conn);
			sc.setAttribute("memberDao", memberDao);
		} catch(Throwable e) {
			e.printStackTrace();
		}
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		try {
			conn.close(); //데이터베이스와 연결 끊기
		} catch (Exception e) {}
	}
}

 

- ServletContextListener 인터페이스의 구현

웹 애플리케이션의 시작과 종료 사건을 처리하려면, 리스너 클래스는 반드시 ServletContextListener 규칙에 따라 작성해야 한다. ContextLoaderListener 클래스는 이 인터페이스를 구현하고 있다.

public class ContextLoaderListener implements ServletContextListener {
    ...
}

 

- contextInitialized() 메서드

contextInitialized() 메서드를 보면, AppInitServlet 클래스에 있던 DB 커넥션 객체를 준비하는 코드를 가져왔다.

//DB 커넥션 객체 준비
ServletContext sc = event.getServletContext();
Class.forName(sc.getInitParameter("driver"));
conn = DriverManager.getConnection(
		sc.getInitParameter("url"),
		sc.getInitParameter("username"),
		sc.getInitParameter("password"));

이 리스너의 핵심은 웹 애플리케이션이 시작될 때 MemberDao 클래스 객체를 준비하여 ServletContext에 보관하는 것이다.

//MemberDao 객체 준비하여 ServletContext에 보관
MemberDao memberDao = new MemberDao();
memberDao.setConnection(conn);
sc.setAttribute("memberDao", memberDao);

 

- contextDestroyed() 메서드

DB 커넥션 객체의 참조 변수 'conn'은 리스너의 인스턴스 변수이다. 인스턴스 변수로 만든 이유는 contextDestroyed() 메서드에서 변수 'conn'을 사용하기 때문이다. 즉 웹 애플리케이션이 종료되기 전에 데이터베이스와의 연결을 끊어야 하기 때문이다.

Connection conn; //인스턴스 변수
    
...
    
@Override
public void contextDestroyed(ServletContextEvent event) {
	try {
		conn.close(); //데이터베이스와 연결 끊기
	} catch (Exception e) {}
}

 

- ContextLoaderListener 클래스의 배치

리스너의 배치는 두 가지 방법이 있다. 첫 번째는 앞의 예제 코드처럼 어노테이션을 사용하는 방법이다. 클래스 선언 위에 @WebListener라고 어노테이션을 붙이면 된다.

//리스너의 배치 : @WebListener 어노테이션 사용
@WebListener
public class ContextLoaderListener implements ServletContextListener {
    ...
}

두 번째는 DD 파일(web.xml)에 XML 태그를 선언하는 것이다.

<!-- 리스너 선언 -->
<listener>
	<listener-class>spms.listeners.ContextLoaderListener</listener-class>
</listener>

앞의 태그를 web.xml 파일의 <web-app> 태그 안에 작성하면 된다. Servlet 2.4 버전 까지는 <listener> 태그를 작성할 때 순서를 지켜야 하는데, <filter-mapping> 태그 다음에, <servlet> 태그 이전에 와야 한다. Servlet 2.5 버전 부터는 순서에 상관없다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글