拦截器模式

背景

拦截器模式是一种行为设计模式,允许在不修改原有对象的情况下,在其前后插入额外的逻辑。Filter 在 Servlet 中的应用正是这一模式的典型体现:

前置处理:在请求到达目标资源(如 Servlet、JSP)之前,对请求进行预处理(如字符编码、权限校验)。
后置处理:在响应返回客户端之前,对响应进行加工(如压缩、日志记录)。

Servlet Filter 使用的设计模式:拦截器模式(Interceptor Pattern)

1. 拦截器模式的核心思想

拦截器模式是一种行为设计模式,允许在不修改原有对象的情况下,在其前后插入额外的逻辑。Filter 在 Servlet 中的应用正是这一模式的典型体现:

  • 前置处理:在请求到达目标资源(如 Servlet、JSP)之前,对请求进行预处理(如字符编码、权限校验)。
  • 后置处理:在响应返回客户端之前,对响应进行加工(如压缩、日志记录)。

2. Filter 的典型应用场景

(1) 全局字符编码过滤

@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        req.setCharacterEncoding("UTF-8");
        res.setCharacterEncoding("UTF-8");
        chain.doFilter(req, res);
    }
}

(2) 登录校验过滤

@WebFilter("/secure/*")
public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        
        if (request.getSession().getAttribute("user") == null) {
            response.sendRedirect(request.getContextPath() + "/login");
        } else {
            chain.doFilter(req, res);
        }
    }
}

(3) 日志记录过滤

@WebFilter("/*")
public class LogFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(LogFilter.class);

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        String uri = ((HttpServletRequest) req).getRequestURI();
        logger.info("Request: {} - {}", LocalDateTime.now(), uri);
        chain.doFilter(req, res);
        logger.info("Response: {} - {}", LocalDateTime.now(), uri);
    }
}

3. 用好 Filter 的最佳实践

(1) 单一职责原则

  • 每个 Filter 专注一个功能(如编码、鉴权、日志),避免功能混杂。
  • 示例:拆分 EncodingFilterSecurityFilter

(2) 控制执行顺序

  • 通过 @WebFilterorder 属性或 web.xml 的 <filter-mapping> 顺序定义优先级。
  • 关键场景:字符编码过滤器应放在最前端。

(3) 异步支持

  • 对耗时操作(如数据库查询)使用 AsyncContext,避免阻塞主线程:
chain.doFilter(req, res); // 同步调用
// 或
AsyncContext asyncContext = req.startAsync();
asyncContext.start(() -> {
    // 异步处理逻辑
});

(4) 资源管理

  • init() 中加载配置,在 destroy() 中释放资源(如数据库连接池):
@Override
public void init(FilterConfig config) throws ServletException {
    // 初始化配置
}

@Override
public void destroy() {
    // 释放资源
}

(5) 异常处理

  • 捕获 Filter 链中的异常并统一处理:
try {
    chain.doFilter(req, res);
} catch (Exception e) {
    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error occurred");
}

4. 进阶技巧

(1) 动态过滤路径

  • 通过注解参数动态匹配路径:
@WebFilter(urlPatterns = {"${config.loginPath}"})
public class DynamicFilter implements Filter { /* ... */ }

(2) CORS 处理

@WebFilter("/*")
public class CorsFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
        chain.doFilter(req, res);
    }
}

(3) 请求/响应修改

  • 修改请求参数(需谨慎):
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(req) {
    @Override
    public String getParameter(String name) {
        return super.getParameter(name).toUpperCase();
    }
};
chain.doFilter(requestWrapper, res);

5. 注意事项

  • 性能影响:避免在 Filter 中进行 IO 操作或复杂计算。
  • 安全风险:敏感操作(如密码验证)应在 Filter 之后由业务层处理。
  • 依赖注入:可通过 @Inject 注入 Spring Bean(需启用 @WebFilter + @Component)。

通过合理设计 Filter 链,可以显著提升代码的可维护性和扩展性,同时保持核心业务逻辑的简洁性。

参考资料

posted @   向着朝阳  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!
点击右上角即可分享
微信分享提示