4.서블릿 생명주기
1. 서블릿 인스턴스의 생명 주기
Java SE 프로그램에서는 main()안에 구현된 순서대로 프로그램이 실행되었음
→ 개발자가 프로그램의 동작 흐름을 제어
Java EE 프로그램은 실행의 흐름을 서블릿 컨테이너(Apache Tomcat)가 제어함
→ 컨테이너가 프로그램의 동작 흐름을 제어, 제어의 역전
서블릿 역시 Java EE에 속하기 때문에 컨테이너가 프로그램, 서블릿을 어떤 순서로 동작시키는지 이해하는 것이 좋음

중요한 점은 클라이언트로부터의 요청이 최초 요청인지 아닌지에 따라 해당 요청에 맞는 서블릿 객체의 생성 여부가 결정됨
기존의 CGI 방식에서는 요청이 있을 때마다 응답을 위한 동작 처리가 매번 독립적으로 처리되었지만,
서블릿은 한 번 생성한 객체를 재사용한다는 효율성이 있음
2. 서블릿 객체 초기화 과정
서블릿 컨테이너에 의해 클라이언트의 요청 경로에 따라 동작하도록 맵핑(mapping)된 서블릿 클래스의 인스턴스가 생성되며,
인스턴스 생성을 위해 서블릿의 생성자가 호출됨
2-1. 서블릿 객체 초기화, init() called
서블릿 객체가 생성되며 초기화 과정이 이루어질 때 별도의 초기화 작업이 필요할 경우 사용할 수 있는 메서드
개발자는 해당 메서드 안에 자신만의 로직을 작성하면 해당 로직은 서블릿 객체가 초기화될 때 같이 실행됨
주로 서블릿이 사용할 리소스를 로드하거나, 설정값을 초기화하는 등의 작업으로 활용함
2-2. 서블릿 요청, service() called
클라이언트로부터 요청(HTTP request)이 있을 때마다 계속 호출되는 메서드
사용자의 요청에 대한 응답 처리 로직과 같이 실제 서블릿이 처리해야 할 작업을 작성하는 메서드
또한 클라이언트가 요청한 정보를 담고 있는 요청 정보를 확인하고,
요청 방식의 종류에 따라 서로 다른 메서드를 호출하여 처리를 수행함
ex)
GET 요청 시 - doGet()에 작성
POST 요청 시 - doPost()에 작성
2-3. 서블릿 객체 제거, destroy() called
서블릿 객체의 종료 작업을 처리할 때 사용됨
서블릿 컨테이너에 의해 생성되었던 서블릿 인스턴스가 메모리에서 제거될 때 같이 처리하고 싶은 작업을 작성할 때 사용할 수 있는 메서드
destroy()가 호출된 후에는 서블릿 인스턴스가 소멸되고 메모리에서 제거되기 때문에 해당 서블릿을 통한 서비스가 불가능해짐
3.HttpServlet.java 내부 코드
HttpServlet의 내부 코드는 다음과 같음
public service(ServletRequest req, ServletResponse res) 로직
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}protected void service(HttpServletRequest req, HttpServletResponse resp) 로직
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
// -> doGET() 호출
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
// -> doPost() 호출
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}