[JSP] ServletContextListener 개념 및 예제, 구현해보기

ServletContextListener

- 웹 어플리케이션이 시작되거나 종료될 때 호출할 메서드를 정의한 인터페이스.

- public void contextInitialized(ServletContextEvent sce) : 웹어플리케이션을 초기화할 때 호출

- public void contextDestroyed(ServletContextEvent sce) : 웹 어플리케이션을 종료할 때 호출.

 

웹 어플리케이션이 시작되고, 종료될 때 특정한 기능을 수행하기 위해서는 아래와 같이 하면 된다.

 1. javax.servlet.ServletContextListener 인터페이스를 구현한 클래스를 작성한다.

 2. web.xml 파일에 1번에서 작성한 클래스를 등록한다.

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
	 http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
 version="3.1">
 
 	<listener>
 		<listener-class>jdbc.DoDBCPInitListener</listener-class>
 	</listener>
 	
 	<context-param>
 		<param-name>connectionPoolConfiguration</param-name>
 		<param-value>
 			jdbcdriver=com.mysql.jdbc.Driver
 			jdbcUrl=jdbc:mysql://localhost:3306/test?characterEncoding=utf8
 			dbUser=testuser
 			dbPass=test123
 		</param-value>
 	</context-param>
 </web-app>

1개 이상의 <listener> 태그를 등록할 수 있으며, 각 <listener> 태그는 반드시 한 개의 <listener-class> 태그를 자식 태그로 가져야 한다. 두개의 listener를 등록할 수도 있는데 이때 실행 순서는 먼저 등록한 순서(위에서 아래로)이다.

두개 이상의 listener가 종료될 때는 아래에서 위의 순서대로 종료한다.(즉, 스택구조이다.)

 

public class DoDBCPInitListener implements ServletContextListener{
	
	@Override
	public void contextInitialized(ServletContextEvent sce) {
		String poolConfig = sce.getServletContext().getInitParameter("connectionPoolConfiguration");
		Properties prop = new Properties();
		try {
			prop.load(new StringReader(poolConfig));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		loadJDBCDriver(prop);
		initConnectionPool(prop);
	}
	
	private void initConnectionPool(Properties prop) {
		try {
			String jdbcUrl = prop.getProperty("jdbcUrl");
			String username = prop.getProperty("dbUser");
			String pw = prop.getProperty("dbPass");
			
			ConnectionFactory connFactory = new DriverManagerConnectionFactory(jdbcUrl,username, pw);
			
			PoolableConnectionFactory poolableConnFactory = new PoolableConnectionFactory(connFactory, null);
			poolableConnFactory.setValidationQuery("select 1");
			
			GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
			poolConfig.setTimeBetweenEvictionRunsMillis(1000L*60L*5L);
			poolConfig.setTestWhileIdle(true);
			poolConfig.setMinIdle(4);
			poolConfig.setMaxTotal(50);
			
			GenericObjectPool<PoolableConnection> connectionPool = new GenericObjectPool<>(poolableConnFactory, poolConfig);
			
			poolableConnFactory.setPool(connectionPool);
			
			Class.forName("org.apache.commons.dbcp2.PoolingDriver");
			PoolingDriver driver = (PoolingDriver)DriverManager.getDriver("jdbc:apache:commons:dbcp:");
			String poolName = prop.getProperty("poolName");
			driver.registerPool(poolName, connectionPool);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
	}

	private void loadJDBCDriver(Properties prop) {
		String driverClass = prop.getProperty("jdbcdriver");
		try {
			Class.forName(driverClass);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("fail to load JDBC Driver", e);
		}
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		
	}
}

위의 web.xml에서 context-param 태그의 키=값 으로 이루어진 파라미터값을 자바에서 getInitParameter( <param-name>명 ) 으로 받을 수 있다. 그것을 Properties 객체로 받게된다. Properties 객체에 load된 키를 이용하여 getProperties("키") 메소드를 이용해서 커넥션풀을 초기화하는 작업을 자바코드로 작성한 것이다.

 

Exception 처리

contestInitialized() 메서드에는 throws가 정의되어 있지 않기 때문에 익셉션을 발생시키려면 RuntimeException이나 그 하위 타입의 익셉션을 발생시켜야 한다.

 

어노테이션을 이용해서 리스너를 등록할 수 있다.

import javax.servlet.annotation.WebListener;

@WebListener
public class TestListener implements ServletContextListener {
	...//내용
}

서블릿 3.0 버전부터는 web.xml에 등록하지 않아도 @WebListener 어노테이션을 리스너로 적용할 클래스(ServletContextListener 인터페이스를 구현하는)에 적용하면 자동으로 리스너로 등록된다.( 클래스에 빨간줄이 그어지면 Add 구현되지 않은 메소드 하면  Override 할 것이다.)

 

 

댓글

Designed by JB FACTORY