The lifecycle of a Servlet is managed by the container and consists of four distinct phases:
- Creation: Occurs once, during the first request.
- Initialization: Happens once, immediately after instantiation.
- Service Execution: Invoked multiple times, once per request.
- Destruction: Executed once, when the container shuts down.
When a client makes the first request to a Servlet, the container instantiates the class, calls its init() method, and then runs the service() method in a new thread to handle the request. After completion, the container does not destroy the instance but keeps it cached. Subsequent requests reuse this cached instance, invoking service() again in separate threads. Before shutting down, the container calls the destroy() method once.
Avoid using instance variables in Servlets unless absolutely necessary—this can lead to thread-safety issues. If modification is required, ensure proper synchronization, though such measures may significantly impact performance. Best practice: minimize or eliminate instance variable usage entirely.
Example Servlet Implementation
package com.mashibing.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet4 extends HttpServlet {
// Instance variable (avoid if possible)
public MyServlet4() {
System.out.println("MyServlet4 Constructor invoked");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void init() throws ServletException {
System.out.println("MyServlet4 init invoked");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("MyServlet4 service invoked");
}
@Override
public void destroy() {
System.out.println("MyServlet4 destroy invoked");
}
}
Servlet Configuration in web.xml
<servlet>
<servlet-name>myServlet4</servlet-name>
<servlet-class>com.mashibing.servlet.MyServlet4</servlet-class>
<load-on-startup>6</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myServlet4</servlet-name>
<url-pattern>/myServlet4.do</url-pattern>
</servlet-mapping>
To verify the lifecycle behavior, make repeated requests to the Servlet and observe the console output. The load-on-startup element ensures the Servlet is instantiated and initializde at server startup. Its value indicates initialization order; use numbers greater than 6 to avoid conflicts.
Request Handling Flow
When a client sends an HTTP GET request, the container invokes the doGet() method in your custom Servlet. This behavior stems from the container parsing web.xml (located in WEB-INF) at startup to map URLs to Servlet classes. Upon receiving a request, the container determines the target URI, locates the corresponding Servlet, and checks if it’s already instantiated. If so, it reuses the existing instance without recreating it.
During instantiation, the container calls init(). Since HttpServlet overrides this method (with an empty body), the default implementation is executed. Then, a new thread executes service(), which is also overridden in HttpServlet. Inside service(), the request method is determined via request.getMethod(). If the method is GET, doGet() is called; for POST, doPost() is invoked. If you override doGet() in your custom Servlet, polymorphism ensures that your version is executed instead of the default one.