第十二章:实现 Facade(外观模式)

功能目标

本章的核心目标是使用 Facade(外观)模式 简化外部对 Servlet API 的访问。通过该模式,我们可以隐藏内部复杂的实现细节,提供更简洁、统一的接口供外部调用,从而降低系统的耦合度。

实现内容

  • Facade 模式概述:Facade 是一种结构型设计模式,用于封装复杂的子系统。它提供一个统一的接口,使得外部系统无需了解内部实现细节即可轻松访问子系统功能。
  • 在 Servlet 容器中的应用:在 Web 容器内部,我们可以通过 Facade 包装 HttpServletRequestHttpServletResponse 等核心对象。这样做不仅限制了对内部结构的直接访问,还简化了对外接口的复杂度。
  • 具体实现类:本章将实现 RequestFacadeResponseFacade 类。它们分别封装了请求和响应对象,隐藏了底层复杂细节,对外提供标准的 Servlet 接口。

示例功能

我们将创建一个 RequestFacade 类,封装实际的请求对象,屏蔽不必要的内部细节,从而简化外部对请求信息的访问流程。


12.1 Facade 模式设计

Facade 模式 的关键在于将复杂的子系统操作封装在一个简单的接口背后。对于 Web 容器而言,我们需要简化客户端访问 HttpServletRequestHttpServletResponse 的过程,同时隐藏底层实现的具体细节。

在本项目中,我们将向用户提供 RequestFacadeResponseFacade 对象,代替直接暴露原始的 HttpServletRequestHttpServletResponse 实现类。


12.2 实现 RequestFacade 类

RequestFacade 类封装了对 HttpServletRequest 的操作,隐藏了请求底层的复杂实现细节。客户端仅通过 RequestFacade 来访问请求的相关信息,无法直接操作底层对象。

package com.daicy.minitomcat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class RequestFacade extends HttpServletRequestWrapper {
    /**
     * Constructs a request object wrapping the given request.
     *
     * @param request
     * @throws IllegalArgumentException if the request is null
     */
    public RequestFacade(HttpServletRequest request) {
        super(request);
    }
}

通过继承 HttpServletRequestWrapperRequestFacade 可以方便地代理以下常用方法:

  • getRequestURI():获取请求的 URI。
  • getHeaderNames():获取请求头的名称枚举。
  • getHeader(String name):根据头名称获取请求头的值。
  • getParameter(String name):获取请求参数。
  • getMethod():获取请求方法(如 GET、POST 等)。

通过封装 HttpServletRequest,外部系统不需要直接与复杂的请求对象交互,只需使用 RequestFacade 提供的简洁接口即可。


12.3 实现 ResponseFacade 类

ResponseFacade 类封装了对 HttpServletResponse 的操作,隐藏了响应的复杂实现细节。客户端通过 ResponseFacade 来操作响应数据,确保响应行为符合容器规范。

package com.daicy.minitomcat;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class ResponseFacade extends HttpServletResponseWrapper {
    /**
     * Constructs a response adaptor wrapping the given response.
     *
     * @param response
     * @throws IllegalArgumentException if the response is null
     */
    public ResponseFacade(HttpServletResponse response) {
        super(response);
    }
}

ResponseFacade 主要代理以下关键方法:

  • setStatus(int statusCode):设置响应的状态码。
  • setHeader(String name, String value):设置响应头。
  • write(String content):将内容写入响应体(通过 Writer 或 OutputStream)。
  • sendRedirect(String location):发送重定向响应。

通过 ResponseFacade,外部系统可以更安全、简单地管理 HTTP 响应。


12.4 使用 Facade 模式简化代码

在 Web 服务器的核心实现中,RequestFacadeResponseFacade 简化了请求和响应的处理流程,让外部调用(如 Filter 链或 Servlet 调用)更加直观且安全。

以下是在处理请求时实例化并使用 Facade 对象的示例代码:

// 创建 Facade 对象包装原始请求和响应
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);

// 应用 Header 处理
headerHandler.applyHeaders(requestFacade, responseFacade, requestFacade.getSession().getId());

// 获取过滤器链
List<Filter> filters = HttpServer.filterManager.getFilters();
FilterChain filterChain = new FilterChain() {
    int index = 0;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (index == filters.size()) {
            try {
                // 执行最终的 Servlet 逻辑
                wrapper.invoke((HttpServletRequest) request, (HttpServletResponse) response);
            } catch (Exception e) {
                HttpServletResponse httpServletResponse = (HttpServletResponse) response;
                // 捕获异常并设置错误状态码
                httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                httpServletResponse.getWriter().write("Internal Server Error: " + e.getMessage());
            }
        } else {
            Filter filter = filters.get(index);
            index++;
            // 递归调用下一个过滤器
            filter.doFilter(request, response, this);
        }
    }
};
// 启动过滤器链,传入 Facade 对象
filterChain.doFilter(requestFacade, responseFacade);
  • ServletProcessor 类中,通过 RequestFacadeResponseFacade 处理请求和响应,使得外部调用变得简单,同时防止用户代码直接操作容器内部对象。

12.5 学习收获

通过实现 Facade 模式,我们获得了以下核心经验:

  1. Facade 模式的设计思想:Facade 模式通过封装复杂的子系统,提供了一个简化的接口,使得外部系统不需要了解内部的实现细节,符合“迪米特法则”(最少知道原则)。
  2. 简化接口设计:通过 RequestFacadeResponseFacade,我们能够把复杂的请求和响应处理逻辑隐藏在内部,只暴露简单易用的接口给外部调用者。
  3. 提高安全性和易用性:Facade 模式通过限制对复杂系统的直接访问,提供了更高的安全性(防止内部状态被意外修改),同时降低了外部系统的使用难度。

通过 Facade 模式,我们使得 Web 服务器的接口变得更加简洁和安全,同时也更易于扩展和维护。

项目源代码地址:

https://github.com/daichangya/MiniTomcat/tree/chapter12/mini-tomcat

说明:本文代码基于 javax.servlet 包(Servlet API 4.0 及以前版本)。在 Jakarta EE 9 及更高版本中,包名已变更为 jakarta.servlet,实际开发中请根据所使用的容器版本调整依赖。