비전공자 공부일기/:: WEB & Front-End

웹개발 - 서블릿 생명주기 Servlet Life Cycle

와니_ 2019. 7. 2. 09:51

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();
		}
	}
}

==> 위 예시를 통해, 서블릿 인스턴스가 사용되는 횟수를 확인해볼 수 있다.