Keywords: Servlet | Multithreading | Session Management | Instantiation | Thread Safety
Abstract: This article provides a comprehensive exploration of Java Servlet core mechanisms, covering Servlet container startup processes, Servlet instantiation strategies, HttpSession session management principles, and thread safety in multithreaded environments. Through detailed analysis of the lifecycle and scope of ServletContext, HttpServletRequest, HttpServletResponse, and HttpSession, combined with practical code examples demonstrating proper usage of instance and session variables, it assists developers in building high-performance, thread-safe web applications.
Servlet Container Startup and Initialization
When the Servlet container (e.g., Apache Tomcat) starts, it deploys and loads all web applications. During the loading of each web application, the container creates a single instance of ServletContext and stores it in server memory. It parses web.xml and web-fragment.xml configuration files, instantiating all defined <servlet>, <filter>, and <listener> elements (or corresponding classes annotated with @WebServlet, @WebFilter, and @WebListener), registering them via the ServletContext.
For Servlets configured with a <load-on-startup> value of 0 or greater, their init() method is invoked during startup in the specified order. If not configured or with a negative value, init() is called upon the first HTTP request. Servlets provide two init() methods: one accepting a ServletConfig parameter (which contains the ServletContext), and another with no parameters but access to the context via the inherited getServletContext() method.
After initialization, the container invokes ServletContextListener#contextInitialized(), allowing developers to programmatically register additional Servlets, Filters, or Listeners. Upon shutdown, the container calls the destroy() method of all initialized components and finally destroys the ServletContext.
HTTP Request Processing Flow
The Servlet container binds to a web server listening on a specific port (commonly 8080 for development and 80 for production). When a client sends an HTTP request, the container creates new instances of HttpServletRequest and HttpServletResponse, passing them through the defined Filter chain to the target Servlet.
The Filter's doFilter() method is invoked; if chain.doFilter(request, response) is called, the request and response proceed to the next Filter or Servlet. The Servlet's service() method, by default, determines which doXxx() method to call based on request.getMethod(), returning an HTTP 405 error if the method is absent.
The request object provides full access to HTTP request details (e.g., URL, headers, query string, and body), while the response object allows setting headers and the response body (typically HTML generated from a JSP). After the response is committed and finished, the request and response objects are recycled for reuse.
HttpSession Session Management
When a client visits the web application for the first time or obtains the session via request.getSession() initially, the container creates a new HttpSession object, generates a unique ID (accessible via session.getId()), and stores it in server memory. Simultaneously, it sets a Cookie in the Set-Cookie header of the HTTP response with JSESSIONID as the name and the session ID as the value.
Adhering to the HTTP Cookie specification, the client includes this Cookie in the Cookie header of subsequent requests as long as it is valid (i.e., the session is unexpired and domain/path are correct). The container checks the Cookie header of each incoming request for the JSESSIONID cookie, using its value to retrieve the associated HttpSession.
The session remains active until idle for longer than the timeout specified in web.xml's <session-timeout> setting (default is typically 30 minutes). After expiration, even with the Cookie, access to the original session is lost, and the container creates a new one. When the client closes the browser instance, the session Cookie is lost, and a new instance does not send the original Cookie, resulting in a new session creation.
Scope and Lifecycle Summary
ServletContext: Lives as long as the web application, shared by all requests across all sessions.HttpSession: Valid during client interaction with the same browser instance, shared by all requests in the same session.HttpServletRequestandHttpServletResponse: Exist from request reception until response completion, not shared elsewhere.- All
Servlet,Filter, andListenerinstances: Live as long as the web application, shared by all requests across all sessions. - Any attribute defined in
ServletContext,HttpServletRequest, orHttpSession: Lives as long as the respective object, representing the scope in bean management frameworks like JSF, CDI, Spring, etc.
Thread Safety Analysis
Servlets and Filters are shared among all requests, leveraging Java's multithreading to avoid the high cost of recreating, initializing, and destroying instances per request. However, it is crucial never to assign request or session-scoped data as instance variables of a Servlet or Filter, as they would be shared by requests from other sessions, leading to thread unsafety. The following code example illustrates this:
public class ExampleServlet extends HttpServlet {
private Object thisIsNOTThreadSafe;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object thisIsThreadSafe;
thisIsNOTThreadSafe = request.getParameter("foo"); // BAD! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread-safe.
}
}
The instance variable thisIsNOTThreadSafe is shared across all requests; if multiple threads modify it concurrently, data races occur. The local variable thisIsThreadSafe is only valid within the current request thread, ensuring thread safety.
Difference Between Session and Instance Variables
Addressing the user's query: When multiple users send requests, session variables are distinct for each user because each has a unique HttpSession. The server differentiates users via the JSESSIONID Cookie. For a specific Servlet accessed by n users, the Servlet is typically instantiated only once (unless configured for on-demand loading), with all users sharing the same instance. Thus, instance variables are shared among all users, requiring careful handling to avoid thread safety issues.