웹개발 - 서블릿 생명주기 Servlet Life Cycle
2019. 07. 01 필기
서블릿 생명주기란?
서블릿은 클라이언트로부터 요청을 받으면 요청에 의해 WAS(Web Application Server)의 컨테이너가 인스턴스를 생성하여 요청 처리 후 응답한다.
=> WAS의 컨테이너가 서블릿 인스턴스의 생성, 사용(=메소드 호출), 소멸에 대한 전반적인 관리를 담당한다.
=> 만약 기존에 생성된 서블릿 인스턴스가 이미 존재할 경우 새로 인스턴스를 생성하지 않는다
=> WAS가 종료되면 컨테이너가 서블릿 인스턴스를 소멸시킨다
=> 이 때, 서블릿 인스턴스의 생성~사용~소멸 과정을 '생명주기(=Life Cycle)'이라고 한다.
서블릿의 생명주기를 확인해보는 예시
@WebServlet(name = "LifeCycleServlet", urlPatterns = { "/life.itwill" })
public class LifeCycleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// 1) 기본 생성자
public LifeCycleServlet() {
System.out.println("# LifeCycleServlet 클래스의 기본 생성자");
}
// 2) init()으로 초기화 --- 생성자 대신 초기화 작업
@Override
public void init(ServletConfig config) throws ServletException {
config.getInitParameter("name");
System.out.println("# LifeCycleServlet 클래스의 init() 메소드 호출");
}
// 3) service()
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("# LifeCycleServlet 클래스의 service() 메소드 호출");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet</title>");
out.println("<body>");
out.println("<h1>서블릿(Servlet)</h1>");
out.println("<hr>");
out.println("<p>Hello, Servlet!!!</p>");
out.println("</body>");
out.println("</html>");
}
// 4) destroy() 오버라이드
@Override
public void destroy() {
System.out.println("# LifeCycleServlet 클래스의 destroy() 메소드 호출");
}
}
1) 생성자 : 인스턴스를 생성하는 특별한 메소드
=> 인스턴스 생성시 생성자를 한 번만 호출 : 초기화 작업(필드 초기값 부여)
=> 서블릿은 인스턴스를 딱 한개만 생성한다(싱글톤) 따라서 생성자도 딱 한개만 필요
----- 자바 공부 초기부터 초기화에 대한 이해가 무척 어려웠는데, 다음과 같이 이해하기로 했다.
달리기 경주가 열렸다. 선수들도, 관중들도 모두 경주는 출발선에서 시작한다는 건 안다. 하지만 안내원이 제때 출발선이 어디인지 안내하고 세워주지 않으면 우왕좌왕 할 것이다. 초기화는 바로 출발선으로 안내해주는 작업이다. 이렇게 이해했더니 마음이 편해졌다.
2) init( ) : 서블릿 인스턴스 생성 후 가장 먼저 자동호출되는 메소드 --- 역시 한 번만 호출됨
=> 생성자 대신 init() 메소드로 초기화 작업을 하는 이유는 ServletConfig 인스턴스를 제공받아 사용하기 때문
--- ServletConfig란? : web.xml 파일에 존재하는 정보를 제공받는 기능이 담긴 인스턴스
--- 이클립스에서 init을 치고 ctrl + space를 누르면 오버라이드 가능한 메소드가 두 가지가 뜨는데, 반드시 ServletConfig 파라미터를 가진 것으로 오버라이드
3) service( ) : 클라이언트의 요청마다 컨테이너에 의해 자동 호출되는 메소드
=> 요청처리 후 응답문서를 생성하여 클라이언트에게 제공하는 메소드
=> doGet() 및 doPost() 메소드도 유사한 작업을 수행하는 메소드
=> 메소드 선언되어 있지 않은 경우 클라이언트에게 405코드 전달
4) destroy( ) : 서블릿 인스턴스가 소멸되기 전에 한 번만 자동 호출되는 메소드
--- 실제로 하는 역할은 없다. 따라서 굳이 오버라이드 하지 않아도 되지만 여기서는 예시를 위해 오버라이드
서블릿 인스턴스의 사용을 확인해볼 수 있는 메소드 예시
//클라이언트의 요청마다 서블릿 요청횟수를 클라이언트에게 제공하는 웹 어플리케이션
@WebServlet("/count.itwill")
public class CountServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/*
* //서블릿의 요청 회수를 누적 저장하기 위한 필드 선언 // => WAS 종료시 인스턴스 소멸 - 필드 소멸 >> 계속 남아있게 하려면?
* 어딘가에 저장해주어야 한다. // >> 어디에? 파일에
* private int cnt; // 필드는 자동 초기화되기 때문에 =0 으로 초기화해주지 않아도 된다.
*/
private int newcount;
//카운터 파일의 절대경로를 저장하기 위한 필드
private String counterFilePath;
//카운터 파일에 저장된 정보를 읽어 필드에 저장
// => 카운터 파일에 없는 경우는 필드에 초기값으로 0 저장
@Override
public void init(ServletConfig config) throws ServletException {
//카운터 파일의 절대경로를 반환받아 저장
// 컨텍스트의 위치, 서버에 올리는 위치는 각기 다르기 때문에 절대경로는 위험 >> 반드시 컨텍스트에게 자원경로를 달라고 해야함
// >> request한테 없으면 config를 사용하면 절대경로 사용 가능
counterFilePath=config.getServletContext().getRealPath("/WEB-INF/counter.txt");
/* System.out.println("counterFilePath = "+counterFilePath); */
try {
//카운터 파일에 대한 입력스트림을 생성하여 저장
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(counterFilePath));
//파일 입력스트림을 이용하여 카운터 정보(서블릿 요청횟수)를 반환받아 필드에 저장
newcount=(Integer)ois.readObject();
//파일 입력스트림 제거
ois.close();
} catch (Exception e) {
newcount=0;
}
}
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=response.getWriter();
//서블릿 요청 횟수를 누적하기 위한 변수
// => 메소드가 종료되면 자동 소멸되는 지역변수 - 변수값 유지 불가능
/* int count=0; */
//서블릿 요청 횟수 누적
newcount++;
/* cnt++; */
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet</title>");
out.println("<body>");
out.println("<h1>서블릿 카운트</h1>");
out.println("<hr>");
/* out.println("<p>잘못된 서블릿 요청횟수 = "+count+"</p>"); */
/* out.println("<p>실제 서블릿 요청횟수 = "+cnt+"</p>"); */
out.println("<p>서블릿 요청횟수 = "+newcount+"</p>");
out.println("</body>");
out.println("</html>");
}
//필드값(서블릿 요청횟수)을 카운터 파일에 저장할 수 있도록 만듬
// => 카운터 파일이 존재하지 않는 경우 파일 생성하여 필드값 저장
@Override
public void destroy() {
try {
//카운터 파일에 대한 출력스트림을 생성하여 저장
ObjectOutputStream oos= new ObjectOutputStream
(new FileOutputStream(counterFilePath));
//파일 출력스트림을 이용하여 카운터 정보(서블릿 요청 횟수)를 반환받아 필드에 저장
oos.writeObject(newcount);
//파일 출력스트림 제거
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
==> 위 예시를 통해, 서블릿 인스턴스가 사용되는 횟수를 확인해볼 수 있다.