Chapter 04 Introduction – Different Ways of Creating a Servlet

Rohit GawandeRohit Gawande
27 min read

Servlet Series: Full Stack Java Development

In our journey through the Servlet Series—part of the larger Full Stack Java Development roadmap—this chapter focuses on an important milestone: understanding the different ways of creating a servlet and why one approach might be preferred over another.

If you’ve been following along, you already know that Servlets act as the backbone of Java-based web applications, bridging the gap between client requests and server-side processing. But in the real world, there isn’t just a single way to build them. Depending on the type of protocol we are dealing with, the complexity of request handling, and the need for maintainability, developers may choose different base classes or interfaces to create a servlet.

This chapter takes you step-by-step from the Servlet interface, through GenericServlet, and finally to the specialized HttpServlet, exploring not just the "how" but the "why" behind each choice. Along the way, we’ll break down real request scenarios—like GET and POST—examine their request object handling, and see exactly how lifecycle methods work under the hood.

By the end of this chapter, you will not only know how to implement a servlet in multiple ways but also have the practical insight to choose the right approach in a production environment. We will be using clear examples, dissecting method calls, and even exploring request parameter handling with both single and multiple values, so that no conceptual gap remains.


Overview of Chapter 04 – Different Ways of Creating a Servlet

This chapter is structured to give you a progressive and detailed understanding, following the exact sequence you’ve written in the raw theory. The sections will flow as follows:

  1. Foundation: Servlet Interface and GenericServlet

    • Introduction to the Servlet interface and its abstract methods.

    • Transition to GenericServlet and the reduction of required methods.

    • Why GenericServlet simplifies development but is not enough for HTTP-specific tasks.

  2. Why HttpServlet Exists

    • Limitations of a single service() method in GenericServlet.

    • The need for request-method-specific handling.

    • Introduction to the HttpServlet class and its method structure.

  3. Understanding GET and POST Requests

    • Common ways GET requests are triggered.

    • How POST requests are sent in web applications.

    • Step-by-step breakdown of form submissions, URL patterns, and query strings.

  4. Implementing GET and POST in HttpServlet

    • Code examples for handling GET requests.

    • Code examples for handling POST requests.

    • Using getParameter() to extract request data.

  5. Life Cycle of an HttpServlet

    • From request submission to container handling.

    • Internal method invocation hierarchy.

    • How the container decides which method to execute.

  6. Case Studies on Method Execution

    • Scenarios with different method combinations in the servlet class.

    • How GET/POST requests behave when certain methods are missing.

  7. Combining Logic for Multiple Request Types

    • The doProcess() approach for shared logic.

    • Dynamically identifying the request method.

  8. Advanced Request Object Handling

    • Retrieving single and multiple parameters.

    • Practical example with a full student registration form.

    • Generating dynamic HTML output from servlet responses.

Alright, Rohit — here’s the Section 1 expansion using your raw theory and the diagram you provided.
I’ve kept it in a professional, paragraph-based, storytelling format, exactly in your flow, and included the diagram with a technical caption.


Section 1 – From Servlet Interface to HttpServlet

When we first approach servlet development in Java, the journey starts with the most fundamental building block: the Servlet interface. This interface defines the contract that any servlet must fulfill, containing five abstract methods. At this level, we are working with the most generic form of a servlet, one that is not tied to any particular communication protocol. Implementing this interface directly gives us full control, but also comes with the burden of providing definitions for all the methods, whether we need them or not. This can make servlet creation unnecessarily verbose for most use cases.

To make the process easier and reduce boilerplate code, Java introduced the GenericServlet abstract class. This class implements the Servlet interface and its methods, except for a single abstract method:

public abstract void service(ServletRequest request, ServletResponse response);

By implementing all other methods and leaving only service() as abstract, GenericServlet drastically simplifies servlet development. Now, instead of implementing all five methods from the Servlet interface, a developer only needs to focus on the one central service() method. This is particularly useful when building protocol-independent web applications because it allows handling of requests in a uniform way.

However, there is a catch. While GenericServlet is great for generic request processing, modern web applications rarely deal with just any protocol. In real-world development, HTTP is the dominant communication protocol. This means requests often need to be processed differently depending on whether they are GET or POST requests. With only one generic service() method, distinguishing between request types and debugging becomes more difficult. This is where HttpServlet comes into play.

The HttpServlet abstract class extends GenericServlet and introduces a structure tailored specifically for handling HTTP requests. It provides dedicated methods such as:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

By separating doGet() and doPost(), HttpServlet allows developers to implement logic that is specific to each HTTP request type. This makes the codebase cleaner, improves readability, and makes debugging much easier. Even though HttpServlet contains no abstract methods, it is still marked as abstract because it serves as a base class intended for extension, not direct instantiation.


The Hierarchy in Action

The relationship between these classes and interfaces can be visualized in the diagram below:

Figure 1 – Class and Interface Hierarchy of HttpServlet

At the top level, we have the Servlet interface, which defines the basic lifecycle methods every servlet must support. GenericServlet implements Servlet, as well as ServletConfig (which provides configuration information to a servlet) and Serializable (which allows servlet objects to be serialized if needed). This makes GenericServlet both protocol-agnostic and easily configurable.

HttpServlet then extends GenericServlet, inheriting all of its capabilities while adding HTTP-specific methods. This layered design allows Java to support multiple protocols through GenericServlet while providing specialized handling for HTTP through HttpServlet.


Section 2 – Why HttpServlet Exists

When we start working with GenericServlet, it feels like we already have a solid foundation for processing web requests. After all, it simplifies the Servlet interface by leaving us with only one method to implement:

public abstract void service(ServletRequest request, ServletResponse response);

This single entry point for request processing might seem convenient at first, especially for applications that are protocol-independent. But here’s the problem: in the real world, almost every Java web application is designed to work over HTTP protocol, and HTTP is not a one-size-fits-all kind of protocol. It supports multiple request methods like GET, POST, PUT, DELETE, and more—each serving a distinct purpose.

When we handle all requests inside one service() method, we are essentially lumping together logic for all request types. This quickly becomes messy. Imagine a situation where both GET and POST requests are hitting the same servlet. Without a clear separation, you’d need conditional checks inside the service() method to figure out which request type arrived:

String methodType = request.getMethod();
if (methodType.equals("GET")) {
    // handle GET
} else if (methodType.equals("POST")) {
    // handle POST
}

While technically possible, this approach is far from elegant. It spreads HTTP-specific logic inside a generic method and makes debugging harder. For example, if a POST request isn’t behaving as expected, you would need to dig through a service() method that also handles GET, PUT, and DELETE requests, making it harder to isolate and fix the issue. The generic nature of GenericServlet becomes a disadvantage when you need precision.


The Need for Request-Method-Specific Handling

HTTP request methods are designed with different semantics.

  • GET requests are generally used to retrieve data and are idempotent, meaning repeated calls should produce the same result.

  • POST requests are meant to submit data to the server, often resulting in a change in state or database updates.

Because of these differences, mixing their handling logic in one method is not only bad for maintainability but can also lead to subtle bugs. For example, if a GET request accidentally triggers logic meant for POST, you could end up unintentionally modifying data when the user only meant to retrieve it.

This is why we need request-method-specific handling—a clean, structured approach where GET-related logic lives in one place, POST-related logic in another, and so on. This not only improves code clarity but also aligns perfectly with HTTP’s intended design.


Enter HttpServlet

To address these challenges, Java provides the HttpServlet abstract class. This class specializes in handling HTTP protocol requests and introduces separate methods for each HTTP request type. The most commonly overridden are:

protected void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

protected void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

With these methods, we no longer need to clutter a single service() method with multiple conditions. Instead, the Servlet container automatically determines the request method type and calls the corresponding method in our servlet class. This means if a GET request comes in, doGet() is called; if a POST request comes in, doPost() is called—without us having to explicitly check the method type.

HttpServlet also provides an HTTP-specific version of the service() method:

protected void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException;

This version takes HttpServletRequest and HttpServletResponse objects instead of the generic ServletRequest and ServletResponse. It acts as a dispatcher, determining the HTTP request method and forwarding the call to the appropriate doXXX() method.

Even though HttpServlet doesn’t declare any abstract methods, it is still marked abstract because it is intended to be extended rather than instantiated directly. Developers implement the specific request-handling methods they need, leaving the rest to the default implementation provided by HttpServlet.


Section 3 – Understanding GET and POST Requests

Now that we know why HttpServlet exists and how it separates request-handling logic, it’s time to focus on the two most common HTTP request types you’ll deal with in servlet development: GET and POST. Even though both are used to send information from a client (like a browser) to a server, their behavior and intended purposes are fundamentally different.


How a GET Request is Sent in a Web Application

In a web environment, GET requests can be triggered in multiple ways. Some of the most common scenarios include:

  1. Typing a URL directly into the browser’s address bar and hitting Enter.

  2. Clicking a hyperlink (<a href="...">) within a webpage.

  3. Submitting a form with method="GET".

  4. Submitting a form without specifying the method attribute, since GET is the default.

When a GET request is made, the browser appends the form data to the URL in the form of a query string. This query string is key-value data, like:

http://localhost:8080/test?username=Rohit

Here, username is the key, and Rohit is the value. Because this information becomes part of the URL, it is visible in the browser’s address bar and also gets stored in the browser history.


How a POST Request is Sent in a Web Application

POST requests are usually generated by submitting a form where method="POST" is explicitly specified. Unlike GET, POST sends data in the request body instead of appending it to the URL. This means the information is not visible in the address bar, making it more suitable for sensitive data like login credentials.

Example form for POST:

<form method="POST" action="./test">
    Enter Name: <input type="text" name="username">
    <br>
    <input type="submit" value="login">
</form>

When the user enters a value (say "Sachin") and clicks the login button, the browser prepares an HTTP request with the data in the body. The request is then sent to the server, which hands it over to the Catalina servlet container in Tomcat. The container uses the action attribute of the form to determine which servlet should handle the request. Here, ./test points to the servlet mapped with the /test URL pattern.


How the Container Routes the Request

When the Catalina container receives a request:

  1. It identifies the servlet to handle the request using the URL pattern (either from web.xml or annotations like @WebServlet).

  2. If the servlet is not yet loaded, it goes through the loading → instantiation → initialization sequence.

  3. The container then determines the HTTP request method type.

  4. If the request is a GET, it calls doGet(); if it is a POST, it calls doPost().

For example, here’s a simple servlet handling both GET and POST:

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;

@WebServlet(urlPatterns="/test")
public class TestServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Request method is of type: GET");
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Request method is of type: POST");
    }
}

Behavioral Differences in Practice

  • If you type a URL directly in the browser or click a link, you’re generating a GET request.

  • If you submit a form with POST, the request type is POST.

  • GET parameters are visible in the URL as a query string, while POST parameters are hidden inside the request body.

When data needs to be fetched without modifying server state, GET is preferred. When data needs to be sent or stored (like a registration form), POST is the safer and more semantically correct option.


Section 4: Implementing GET and POST in HttpServlet

When working with Java Servlets, one of the most important tasks is handling client requests based on the HTTP method used. In the HTTP protocol, the two most common request methods are GET and POST. Both serve distinct purposes and are handled differently inside an HttpServlet.

The GenericServlet class, as seen in the servlet hierarchy diagram, provides only a single service() method for handling requests, without distinguishing between request types. This design works for generic protocols but is not sufficient for HTTP-specific requirements. For instance, in web applications, GET and POST requests have entirely different use cases — GET is often used for retrieving data, while POST is used for submitting data to the server.

To address this, the HttpServlet class, which extends GenericServlet, introduces a method-specific handling mechanism by defining dedicated methods such as doGet() for GET requests and doPost() for POST requests. The CATALINA container determines the request type and automatically calls the corresponding method in the servlet.


4.1 Handling GET Requests in HttpServlet

A GET request is typically used to retrieve resources from the server. When a form is submitted with method="GET" or when a user directly enters a URL in the browser, the form data is appended to the request URL as a query string.

For example, if a form field has the name username and the value Rohit, the browser sends the following request:

http://localhost:9999/MyApp/test?username=Rohit

Here, username=Rohit is the query string.

Example: Handling GET requests in HttpServlet

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet("/test")
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Request method is of type: GET");

        // Extracting request parameter from query string
        String username = request.getParameter("username");
        System.out.println("Username is: " + username);
    }
}

Explanation:

  1. The container detects the request method as GET.

  2. It calls the doGet() method in HttpServlet.

  3. The form data is passed to the servlet via the HttpServletRequest object.

  4. The getParameter("username") method retrieves the value of the input field named "username".


4.2 Handling POST Requests in HttpServlet

A POST request is used to send data to the server in the request body, not as part of the URL. This is often preferred for sensitive information (such as passwords) or when sending large amounts of data.

When a form is submitted with method="POST", the browser sends the request without appending parameters to the URL. For example, if the username entered is Virat, the URL might still look like:

http://localhost:9999/MyApp/test

but the username=Virat data will be present in the HTTP body.

Example: Handling POST requests in HttpServlet

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("Request method is of type: POST");

    // Extracting request parameter from request body
    String username = request.getParameter("username");
    System.out.println("Username is: " + username);
}

Explanation:

  1. The container detects the request method as POST.

  2. It calls the doPost() method in HttpServlet.

  3. The form data is included in the request body instead of the URL.

  4. The same getParameter() method works here — the container takes care of parsing both GET query strings and POST request bodies.


4.3 Using getParameter() to Extract Request Data

The getParameter() method is defined in the ServletRequest interface and is available in HttpServletRequest. It is used to retrieve the value of a form field sent by the client.

Syntax:

String value = request.getParameter("fieldName");
  • fieldName → This must match the name attribute of the HTML form input element.

  • If multiple parameters have the same name (e.g., checkboxes), use getParameterValues() to retrieve them as a String array.

Example with HTML form:

<form method="GET" action="./test">
    Enter Name: <input type="text" name="username" />
    <input type="submit" value="Submit" />
</form>

If the user types "Rohit" in the text field, then:

request.getParameter("username"); // returns "Rohit"

4.4 Flow from Browser to Servlet

When a user interacts with a web page that sends data to a servlet, there is a clear sequence of steps from browser → server → container → servlet. The uploaded diagram illustrates this process. Let’s break it down step-by-step.


1. User Action (Browser Side)

  • Suppose we have an HTML form for login:
<form method="POST" action="./test">
    Enter Name: <input type="text" name="username">
    <br>
    <input type="submit" value="login">
</form>
  • Key attributes in the <form> tag:

    • method: Specifies the HTTP method (GET or POST).

    • action: Defines the relative URL (./test) that maps to a servlet in the project.

  • The name attribute of <input> (username here) becomes the key for sending data.

  • If the user types Rohit and clicks Login, the browser will prepare the request:

    • For GET: Data is sent as a query string in the URL, e.g., ?username=Rohit.

    • For POST: Data is sent inside the request body (no query string in the URL).


2. HTTP Request Sent to Tomcat Server

  • The browser sends the HTTP request (method, URL, headers, and body if POST) to the Tomcat server.

  • Tomcat receives the request and passes it to the Catalina container for processing.


3. Catalina Container Processing

Inside Catalina:

  1. Read the Request Line

    • Extracts method (GET or POST) and the target path (/test).
  2. Match URL Pattern

    • Catalina checks web.xml or the @WebServlet annotation to find which servlet corresponds to /test.

    • Example mapping in web.xml:

        <servlet>
            <servlet-name>TestServlet</servlet-name>
            <servlet-class>com.example.TestServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>TestServlet</servlet-name>
            <url-pattern>/test</url-pattern>
        </servlet-mapping>
      
  3. Determine Which Method to Call

    • If the request method is GET, Catalina calls the servlet’s doGet() method.

    • If the request method is POST, Catalina calls the servlet’s doPost() method.


4. Servlet Execution

  • When Catalina calls your servlet, it creates:

    • An HttpServletRequest object (contains request data like form parameters, headers, and method).

    • An HttpServletResponse object (used to send data back to the client).

  • Inside doPost() or doGet():

    • You use request.getParameter("username") to retrieve the submitted value.

    • For example:

        @WebServlet("/test")
        public class TestServlet extends HttpServlet {
            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
                String name = request.getParameter("username");
                response.getWriter().println("Hello " + name);
            }
        }
      
    • If the user entered Rohit, request.getParameter("username") returns "Rohit".


5. Response Sent Back to Browser

  • The servlet processes the request (e.g., validates the username, fetches from a database, etc.).

  • Using HttpServletResponse, it sends the response HTML or JSON back to the client.

  • The browser then renders the response for the user.


Key Points

  • The action attribute in <form> is critical—it tells the browser which URL pattern to target.

  • URL patterns map to specific servlet classes, either through web.xml or annotations.

  • HttpServletRequest is the bridge between the HTML form and servlet code—it holds form data as key-value pairs.

  • Method-specific handling (doGet() vs. doPost()) improves code organization and separation of logic.


6. Demonstration Output

If you send:


7. Key Points for Interviews

  • GET requests are idempotent and used for data retrieval. Parameters are sent in the URL.

  • POST requests are used for data submission, parameters sent in the request body.

  • The action attribute in the HTML form specifies the URL where the request should be sent.

  • The name attribute in form fields defines the key for the parameter map in HttpServletRequest.

  • getParameter() retrieves parameter values regardless of whether the request is GET or POST.


Section 5 – Life Cycle of an HttpServlet

Understanding the life cycle of an HttpServlet is one of the most important concepts for mastering Java web application development. Whether you are preparing for interviews or working on real-world projects, this knowledge forms the backbone of troubleshooting and optimization. In this section, we will walk through the complete journey of a request—from the moment it leaves the browser to the point where the servlet sends a response back—while also exploring the internal method invocation hierarchy and the decision-making process used by the servlet container.


1. From Request Submission to Container Handling

When a client (usually a web browser) sends a request to a server, the life cycle of an HttpServlet begins. Let’s imagine a simple scenario: a user fills in a form on a web page and clicks the “Submit” button. The browser now constructs an HTTP request and sends it to the web server.

The web server’s first task is to determine what type of resource the request is asking for. If it’s a static resource (such as an HTML file, CSS, or image), the server will simply locate it and send it back to the client. This is a straightforward copy-and-paste action: the server fetches the file from disk and streams it back.

However, if the request is for dynamic content—such as invoking business logic, querying a database, or generating HTML on the fly—the server cannot handle this directly. Instead, it hands over control to a web container (in the case of Apache Tomcat, this container is known as Catalina).

The container plays a crucial role: it matches the request’s URL pattern with a servlet class. This mapping can be configured either in web.xml or via @WebServlet annotations in the servlet code. Once it identifies the correct servlet—for example, /test mapping to TestServlet—it checks whether an instance of that servlet already exists.

If the servlet instance does not exist, the container performs the initialization phase:

  1. Loading – The servlet’s .class file is loaded into memory. This step triggers any static blocks inside the class.

  2. Instantiation – The servlet object is created by calling its constructor.

  3. Initialization – The init() method is called once in the servlet’s life cycle, allowing it to set up resources, configurations, or connections.


2. Request Processing Phase

Once the servlet is loaded and initialized, the container moves into the request processing phase. This is where the service() methods come into play. The container starts by creating request and response objects—these are instances of HttpServletRequest and HttpServletResponse (although the base interface types ServletRequest and ServletResponse are used initially).

The container then calls:

public void service(ServletRequest request, ServletResponse response)
    throws ServletException, IOException

At this point, the container checks if the servlet class itself has overridden this public service() method.

  • If it has: the container executes your servlet’s service() method directly, and whatever logic you’ve written inside will handle all request types (GET, POST, etc.).

  • If it hasn’t: the container falls back to HttpServlet’s default implementation of service(ServletRequest, ServletResponse).


3. Internal Method Invocation Hierarchy

When the container calls HttpServlet’s service(ServletRequest, ServletResponse) method, something important happens—it downcasts the generic request and response objects into HTTP-specific ones:

HttpServletRequest hreq = (HttpServletRequest) request;
HttpServletResponse hresp = (HttpServletResponse) response;
service(hreq, hresp); // Calls the protected version

Now, the protected service(HttpServletRequest, HttpServletResponse) method is in control. This is where the HTTP method dispatching logic exists. The method does the following:

  1. Extracts the request method type:

     String requestType = request.getMethod();
    
  2. Determines which method to call:

    • If requestType equals "GET", it calls doGet(request, response).

    • If requestType equals "POST", it calls doPost(request, response).

    • If it’s any other HTTP method and there’s no handler, it returns a 501 Not Implemented status.

This hierarchy ensures that:

  • public service(ServletRequest, ServletResponse) is at the top of the chain.

  • protected service(HttpServletRequest, HttpServletResponse) acts as the dispatcher.

  • doGet() and doPost() methods handle the actual business logic.


4. How the Container Decides Which Method to Execute

The container’s decision-making process is systematic and rule-based:

  1. Check for public service(ServletRequest, ServletResponse) in your servlet class
    If found, it handles all request types—GET, POST, PUT, DELETE—without method-specific differentiation.

  2. If not found, check for protected service(HttpServletRequest, HttpServletResponse) in your servlet class
    If found, it will be executed for all request types, but now you have access to HTTP-specific methods and headers.

  3. If neither service method is overridden, the container uses HttpServlet’s default protected service() method to call doGet(), doPost(), or another doXXX() method based on the request type.

  4. If the relevant doXXX() method is not present in your servlet class, the container executes the parent HttpServlet version of that method, which returns a 405 Method Not Allowed status.


5. Summary of Method Call Flow

The call flow can be visualized like this:

public service(ServletRequest, ServletResponse)
        ↓
protected service(HttpServletRequest, HttpServletResponse)
        ↓
   doGet() / doPost() / doPut() / doDelete()

This design allows maximum flexibility—you can choose to handle all requests in a single method, intercept them in the HTTP-specific service() method, or break them down into individual doXXX() methods for cleaner separation.


In the next section, we will study real-world method execution cases, including what happens when certain methods are missing, and how this affects GET and POST requests. This will bring clarity to why method hierarchy matters in servlet programming.


Section 6 – Case Studies on Method Execution in HttpServlet

Now that we understand the life cycle of an HttpServlet and the internal method invocation hierarchy, it’s time to explore how these concepts play out in real execution scenarios. In this section, we’ll walk through different cases where the presence or absence of certain methods in your servlet affects how GET and POST requests are handled.

These cases are not just theoretical—they occur frequently in real-world development and are often tested in interviews. By the end of this section, you’ll know exactly how the container behaves in each situation.


Case 1 – Overriding Only public service(ServletRequest, ServletResponse)

In this scenario, your servlet class only overrides the public service() method from the Servlet interface:

@Override
public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException {
    System.out.println("Public service() called for all requests");
}

Here’s what happens:

  • For every incoming request—whether GET, POST, PUT, or DELETE—the container will execute your public service() method.

  • The container will not proceed to protected service(HttpServletRequest, HttpServletResponse) or any doXXX() methods because you’ve intercepted the flow at the very top.

  • This approach is useful if you want a single entry point for all request types without method-specific handling.


Case 2 – Overriding Both public service() and protected service()

If your servlet overrides both:

@Override
public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException {
    System.out.println("Public service() called");
}

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("Protected service() called");
}

The container’s choice is clear:

  • The public service() method takes precedence.

  • The protected service() method is never called, even though it exists.

  • Again, no doGet() or doPost() methods will be reached.

This case demonstrates that the public service method has the highest priority in the execution chain.


Case 3 – Overriding protected service() and doGet()

Suppose your servlet looks like this:

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("Protected service() called");
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doGet() called");
}

Here’s the flow:

  • Regardless of whether the request type is GET or POST, the container will call your protected service() method directly.

  • The doGet() method will never be invoked, even for GET requests.

  • This is because once you override the HTTP-specific service() method, you are taking full control of request dispatching.


Case 4 – Sending a GET Request When Only doPost() Exists

If you send a GET request but your servlet only has:

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doPost() called");
}

The container’s behavior:

  • It checks for doGet() in your servlet.

  • Since it is missing, the container falls back to HttpServlet’s default doGet() implementation.

  • The default doGet() sends a 405 Method Not Allowed response, informing the client that GET is unsupported for this URL.


Case 5 – Sending a POST Request When Only doGet() Exists

Similarly, if you send a POST request but your servlet only has:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doGet() called");
}
  • The container looks for doPost() in your servlet.

  • If it is missing, it calls HttpServlet’s default doPost() method.

  • The result is the same as Case 4—a 405 Method Not Allowed status.


Case 6 – Common Logic for Both GET and POST Requests

Often, you might need the same processing logic for both GET and POST requests. Writing the same code in doGet() and doPost() would be repetitive. Instead, you can extract the shared logic into a separate method, often called doProcess():

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doProcess(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doProcess(request, response);
}

private void doProcess(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("Request method is: " + request.getMethod());
    String userName = request.getParameter("username");
    System.out.println("Username is: " + userName);
}

With this design:

  • GET requests call doGet() which in turn calls doProcess().

  • POST requests call doPost() which also calls doProcess().

  • The request method can still be identified using request.getMethod() if needed.

This approach is cleaner and avoids duplication while keeping method separation intact.


Why These Cases Matter

Understanding these cases is crucial because:

  • They reveal how the servlet container prioritizes methods.

  • They help you avoid confusion when a method you expect to run is never called.

  • They are a favorite topic in interviews because they test both theory and practical debugging ability.


Section 7 – Combining Logic for Multiple Request Types & Advanced Request Handling

In modern web applications, a servlet often needs to handle both GET and POST requests for the same resource. While the methods doGet() and doPost() allow you to separate handling logic, there are many cases where you need shared business logic regardless of the request type. At the same time, you must know how to extract request data correctly to make the servlet truly dynamic.

This section covers:

  1. Combining request handling logic for GET and POST.

  2. Retrieving request parameters (single and multiple values).

  3. Detecting the request method at runtime.

  4. A complete real-world student registration form example.


1. Combining GET and POST Logic in Servlets

While you could write identical code in both doGet() and doPost(), this leads to redundancy. A better approach is to create a common method—often named doProcess()—that both doGet() and doPost() call.

Example:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doProcess(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doProcess(request, response);
}

private void doProcess(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();

    out.println("<h3>Request Method: " + request.getMethod() + "</h3>");
    String userName = request.getParameter("username");
    out.println("<p>Username: " + userName + "</p>");
}

How it works:

  • Both GET and POST requests reach the same processing method.

  • You can still differentiate them using request.getMethod() if you need method-specific branching inside doProcess().


2. Retrieving Request Parameters

Parameters sent from an HTML form are passed as name–value pairs in the request.
You can retrieve them using:

  • Single-value parameter:

      String value = request.getParameter("fieldName");
    
  • Multiple values (e.g., checkboxes):

      String[] values = request.getParameterValues("fieldName");
    

3. Detecting the Request Method at Runtime

You can check the request method in any servlet method:

String method = request.getMethod();
if ("GET".equalsIgnoreCase(method)) {
    // GET-specific logic
} else if ("POST".equalsIgnoreCase(method)) {
    // POST-specific logic
}

This is especially useful if you choose not to separate methods but still want method-aware processing.


4. Real-World Example – Student Registration Form

Let’s put it all together with a working example.

HTML Form (studentForm.html)

<!DOCTYPE html>
<html>
<head>
    <title>Student Registration</title>
</head>
<body>
    <h2>Student Registration</h2>
    <form action="register" method="post">
        Name: <input type="text" name="name"><br><br>
        Age: <input type="number" name="age"><br><br>
        Gender: 
        <input type="radio" name="gender" value="Male"> Male
        <input type="radio" name="gender" value="Female"> Female<br><br>
        Subjects: 
        <input type="checkbox" name="subjects" value="Math"> Math
        <input type="checkbox" name="subjects" value="Science"> Science
        <input type="checkbox" name="subjects" value="English"> English<br><br>
        <input type="submit" value="Register">
    </form>
</body>
</html>

Servlet (StudentRegisterServlet.java)

import java.io.*;
import jakarta.servlet.*;
import jakarta.servlet.http.*;

public class StudentRegisterServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doProcess(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doProcess(request, response);
    }

    private void doProcess(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();

        String name = request.getParameter("name");
        String age = request.getParameter("age");
        String gender = request.getParameter("gender");
        String[] subjects = request.getParameterValues("subjects");

        out.println("<html><body>");
        out.println("<h2>Student Registration Details</h2>");
        out.println("<p>Name: " + name + "</p>");
        out.println("<p>Age: " + age + "</p>");
        out.println("<p>Gender: " + gender + "</p>");

        out.println("<p>Subjects: ");
        if (subjects != null) {
            for (String subject : subjects) {
                out.print(subject + " ");
            }
        } else {
            out.print("No subjects selected");
        }
        out.println("</p>");

        out.println("<p>Request Method Used: " + request.getMethod() + "</p>");
        out.println("</body></html>");
    }
}

5. How the Flow Works

  1. User fills in the HTML form and clicks Submit.

  2. The browser sends a POST request to /register (as defined in action).

  3. The container maps /register to StudentRegisterServlet.

  4. The servlet calls doPost() → which calls doProcess().

  5. Parameters are extracted, and an HTML response is generated dynamically.


6. Key Takeaways

  • One method for both GET and POST (doProcess()) is a clean, maintainable approach.

  • request.getParameter() retrieves single values; getParameterValues() retrieves arrays.

  • Always check for null in multi-value parameters to avoid NullPointerException.

  • The request method can be detected programmatically with getMethod(), even inside a shared method.


0
Subscribe to my newsletter

Read articles from Rohit Gawande directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rohit Gawande
Rohit Gawande

🚀 Tech Enthusiast | Full Stack Developer | System Design Explorer 💻 Passionate About Building Scalable Solutions and Sharing Knowledge Hi, I’m Rohit Gawande! 👋I am a Full Stack Java Developer with a deep interest in System Design, Data Structures & Algorithms, and building modern web applications. My goal is to empower developers with practical knowledge, best practices, and insights from real-world experiences. What I’m Currently Doing 🔹 Writing an in-depth System Design Series to help developers master complex design concepts.🔹 Sharing insights and projects from my journey in Full Stack Java Development, DSA in Java (Alpha Plus Course), and Full Stack Web Development.🔹 Exploring advanced Java concepts and modern web technologies. What You Can Expect Here ✨ Detailed technical blogs with examples, diagrams, and real-world use cases.✨ Practical guides on Java, System Design, and Full Stack Development.✨ Community-driven discussions to learn and grow together. Let’s Connect! 🌐 GitHub – Explore my projects and contributions.💼 LinkedIn – Connect for opportunities and collaborations.🏆 LeetCode – Check out my problem-solving journey. 💡 "Learning is a journey, not a destination. Let’s grow together!" Feel free to customize or add more based on your preferences! 😊