Generic Servlet Dispatcher Using Reflection for Method Routing

public class GenericServletDispatcher extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        
        String action = request.getParameter("action");
        
        if (action != null && !action.trim().isEmpty()) {
            try {
                // Resolve target method with exact signature: HttpServletRequest, HttpServletResponse
                Method target = getClass().getMethod(action, HttpServletRequest.class, HttpServletResponse.class);
                
                // Invoke the matched method on current instance
                target.invoke(this, request, response);
                
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                throw new ServletException("Failed to dispatch to method: " + action, e);
            }
        } else {
            throw new ServletException("Missing 'action' parameter in request");
        }
    }
}

Without such a dispatcher, each servlet must manually handle HTTP method routing and parameter decoding:

@WebServlet("/product")
public class ProductController extends HttpServlet {
    private final ProductService service = new ProductServiceImpl();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        
        String op = req.getParameter("action");
        
        switch (op) {
            case "fetch":
                fetchProducts(req, resp);
                break;
            case "search":
                searchProducts(req, resp);
                break;
            default:
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown action");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }

    private void fetchProducts(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        List<Product> items = service.findAll();
        req.setAttribute("products", items);
        req.getRequestDispatcher("/list.jsp").forward(req, resp);
    }

    private void searchProducts(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        String keyword = req.getParameter("q");
        List<Product> results = service.findByKeyword(keyword);
        req.setAttribute("results", results);
        req.getRequestDispatcher("/search.jsp").forward(req, resp);
    }
}

The GenericServletDispatcher eliminates boilerplate by centralizing request decoding, encoding setup, and dynamic method dispatch. Subclasses inherit this behavior and expose public handler methods accepting only HttpServletRequest and HttpServletResponse. The this reference inside service() always resolves to the concrete subclass instance — enabling polymorphic method resolution at runtime.

Reflection is used via two core operaitons:

  • Class.getMethod(String name, Class<?>...): Locates a public, declared method matching name and parameter types — here constrained too (HttpServletRequest, HttpServletResponse).
  • Method.invoke(Object target, Object...): Executes the resolved method on the current servlet instance, passing the typed request/response objects.

Because service(HttpServletRequest, HttpServletResponse) is invoked by the container, the parameters passed into invoke() are already the correct subtypes — no unsafe casting is required. This ensures type safety while preserving flexibility across handler implementations.

Tags: servlet reflection HTTP java-web design-pattern

Posted on Wed, 16 Sep 2026 16:11:10 +0000 by northk