JavaWeb快速入门: Filter与Listener核心详解

本文纲要

  1. Filter概述
  2. Filter快速入门
    开发步骤
    项目结构
    核心代码
  3. Filter执行流程
    流程图解
    代码演示
    数据处理时机
  4. Filter拦截路径配置
    四种配置方式
    示例演示
  5. 过滤器链
    概念与执行流程
    优先级规则
    示例代码与输出
  6. 案例:登录验证过滤器
    需求分析
    项目结构
    登录验证流程
    完整代码解析
  7. Listener概述
  8. Listener基本使用
    开发步骤
    代码示例
  9. 总结

Filter概述

在JavaWeb中,有三大核心组件:ServletFilterListener
Filter(过滤器) 的作用是拦截对Web资源的请求,并在请求到达目标资源前或响应返回客户端前进行一些通用的、重复性的处理。

常见应用场景包括:

  • 登录验证:统一判断用户是否已登录
  • 权限控制:细粒度访问权限校验
  • 统一编码处理:避免每个Servlet重复设置编码
  • 敏感词过滤:对请求参数或响应内容进行过滤

过滤器的工作模型如下:

拦截

客户端请求

Filter

预处理

目标资源

后处理

客户端响应

凡是访问Web应用中的资源(Servlet、JSP、HTML等),都可以先经过过滤器,再决定是否放行。

Filter快速入门

1 ) 开发步骤

  1. 定义一个类,实现 javax.servlet.Filter 接口
  2. 重写三个方法:init()doFilter()destroy()
    • init():初始化时调用
    • doFilter():每次拦截请求时执行,是过滤器的核心
    • destroy():销毁时调用
  3. 使用 @WebFilter 注解配置拦截路径(注意:这里的路径是拦截资源的路径,不是过滤器的访问路径)
  4. doFilter() 中编写逻辑,并通过 chain.doFilter(request, response) 放行

2 ) 项目结构

filter-demo 
├── pom.xml 
├── src 
│   └── main 
│       ├── java/com/wb/web/filter 
│       │   ├── FilterDemo.java 
│       │   ├── FilterDemo2.java 
│       │   └── AFilterDemo2.java 
│       └── webapp 
│           ├── hello.jsp 
│           ├── index.jsp 
│           └── WEB-INF/web.xml 

3 ) 核心代码

3.1 定义一个简单的过滤器

package com.wb.web.filter;
 
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
 
@WebFilter("/*")
public class FilterDemo implements Filter {
 
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        // 1. 放行前,对 request 数据进行处理 
        System.out.println("1.FilterDemo...");
 
        // 放行 
        chain.doFilter(request, response);
 
        // 2. 放行后,对 Response 数据进行处理 
        System.out.println("5.FilterDemo...");
    }
 
    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { }
 
    @Override 
    public void destroy() { }
}

注意:@WebFilter("/*") 表示拦截所有资源。如果拦截后不调用 chain.doFilter(),请求将不会到达目标资源。

3.2 目标 JSP(hello.jsp)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title></head>
<body>
    <h1>hello JSP~</h1>
    <%
        System.out.println("3.hello jsp");
    %>
</body>
</html>

访问 hello.jsp 时,控制台输出顺序为:1.FilterDemo... → 3.hello jsp → 5.FilterDemo...,证明过滤器先执行放行前逻辑,再访问资源,最后执行放行后逻辑。

Filter执行流程

过滤器的执行流程可以概括为:放行前逻辑 → 放行 → 访问资源 → 放行后逻辑

目标资源过滤器客户端目标资源过滤器客户端放行前逻辑执行业务逻辑放行后逻辑请求chain.doFilter() 放行返回响应

代码演示

在过滤器 FilterDemo 和目标 JSP 中分别打印标记:

// FilterDemo.java 
System.out.println("1.FilterDemo...");   // 放行前 
chain.doFilter(request, response);
System.out.println("5.FilterDemo...");   // 放行后 
<%-- hello.jsp --%>
<%
    System.out.println("3.hello jsp");
%>

输出结果:1.FilterDemo... → 3.hello jsp → 5.FilterDemo...,验证了上述执行顺序。

数据处理时机

  • 放行前:一般对 request 数据进行处理(如编码设置、参数过滤)。因为此时 response 还没有业务数据。
  • 放行后:一般对 response 数据进行处理(如压缩、敏感词替换)。因为资源执行完毕后 response 中才填充了真正的响应内容。

Filter拦截路径配置

使用 @WebFilter 注解的 value 属性(或 urlPatterns)配置拦截路径,支持四种方式:

配置方式示例说明
具体资源路径拦截"/login.jsp"只有访问 login.jsp 时过滤器才生效
目录拦截"/user/*"访问 /user/ 下所有资源时触发
后缀名拦截"*.jsp"访问所有 .jsp 后缀资源时触发
拦截所有"/*"任何资源请求都会被过滤(最常用)

示例演示

1 ) 拦截特定资源

@WebFilter("/hello.jsp")
public class FilterDemo2 implements Filter {
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("FilterDemo2 executed...");
        chain.doFilter(request, response);
    }
    //...
}

此时只有访问 hello.jsp 时控制台才输出信息,访问 index.jsp 则不触发过滤器。

2 ) 拦截所有资源

@WebFilter("/*")
public class FilterDemo implements Filter { ... }

这种方式在统一登录验证、编码处理等场景下最常用。

过滤器链

1 ) 概念与执行流程

一个Web应用可以配置多个过滤器,它们按照一定顺序形成一个过滤器链。请求会依次经过每个过滤器,直到最后一个过滤器放行后到达目标资源,返回时再按相反顺序执行放行后的逻辑。

客户端请求

Filter1 放行前

Filter2 放行前

目标资源

Filter2 放行后

Filter1 放行后

客户端响应

2 ) 优先级规则

当使用 @WebFilter 注解配置时,过滤器的执行顺序由类名字符串的自然排序决定。例如:

  • AFilterDemo2 会优于 FilterDemo 执行
  • FilterDemo 会优于 FilterDemo2 执行(因为 Demo < Demo2

3 ) 示例代码与输出

过滤器1:FilterDemo

@WebFilter("/*")
public class FilterDemo implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("1.FilterDemo...");
        chain.doFilter(request, response);
        System.out.println("5.FilterDemo...");
    }
    //...
}

过滤器2:FilterDemo2

@WebFilter("/*")
public class FilterDemo2 implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        System.out.println("2.FilterDemo...");
        chain.doFilter(request, response);
        System.out.println("4.FilterDemo...");
    }
    //...
}

访问任意资源时,控制台输出:

1.FilterDemo...
2.FilterDemo...
3.hello jsp 
4.FilterDemo...
5.FilterDemo...

体现了 FilterDemo(类名字典序更小)先执行,且放行后按反向顺序执行。

案例:登录验证过滤器

1 ) 需求分析

在没有登录验证时,用户可以不登录直接访问品牌列表等受保护资源,登录功能形同虚设。
我们需要编写一个全局过滤器,对所有资源进行登录校验:

  • 已登录:放行
  • 未登录:跳转到登录页面,并提示“您尚未登录”

同时,必须将登录/注册相关的资源直接放行,否则这些页面的CSS、图片等也会被拦截,导致无法登录。

2 ) 项目结构

brand-demo 
├── pom.xml 
├── src/main/java/com/wb 
│   ├── web 
│   │   ├── filter 
│   │   │   └── LoginFilter.java         # 登录验证过滤器 
│   │   ├── listener 
│   │   │   └── ContextLoaderListener.java # 监听器示例 
│   │   ├── LoginServlet.java 
│   │   ├── RegisterServlet.java 
│   │   ├── SelectAllServlet.java 
│   │   └── ... (其他Servlet)
│   └── ... (service, mapper, pojo, util)
└── src/main/webapp 
    ├── login.jsp 
    ├── register.jsp 
    ├── brand.jsp 
    └── ...

3 ) 登录验证流程

收到请求

请求路径是否
与登录/注册相关?

直接放行

Session中
是否有user对象?

放行

存储提示信息
转发到login.jsp

4 ) 完整代码解析

LoginFilter.java

package com.wb.web.filter;
 
import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
 
@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws ServletException, IOException {
        HttpServletRequest req = (HttpServletRequest) request;
 
        // 1. 判断访问路径是否与登录/注册相关(应该直接放行)
        String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet",
                         "/register.jsp","/registerServlet","/checkCodeServlet"};
        String url = req.getRequestURL().toString();
 
        for (String u : urls) {
            if (url.contains(u)) {
                // 直接放行,不再执行后续判断 
                chain.doFilter(request, response);
                return;
            }
        }
 
        // 2. 判断Session中是否存在user对象 
        HttpSession session = req.getSession();
        Object user = session.getAttribute("user");
 
        if (user != null) {
            // 已登录,放行 
            chain.doFilter(request, response);
        } else {
            // 未登录,存储提示信息并跳转到登录页 
            req.setAttribute("login_msg", "您尚未登录!");
            req.getRequestDispatcher("/login.jsp").forward(req, response);
        }
    }
 
    public void init(FilterConfig config) throws ServletException {}
    public void destroy() {}
}

要点分析:

  • 先将 ServletRequest 强转为 HttpServletRequest,以便获取 Session 和请求路径。
  • 白名单路径使用 return 直接退出方法,避免代码继续向下执行。
  • 如果用户未登录,设置 login_msg 属性后转发到 login.jsp,前端通过 ${loginmsg} 显示提示。
  • 登录成功后,LoginServlet 会将 user 对象存入 Session,后续请求就能通过过滤器。

5 ) 登录成功后设置 Session:

// LoginServlet 片段 
HttpSession session = request.getSession();
session.setAttribute("user", user);

这样,过滤器就可以通过 session.getAttribute("user") 判断登录状态。

Listener概述

Listener(监听器) 是 JavaWeb 三大组件之一,用于监听 ServletContextHttpSessionServletRequest 等域对象的创建销毁以及属性变化。当监听到指定事件时会自动执行相应方法。

虽然现在监听器使用频率不如 Filter 和 Servlet 高,但在框架初始化等场景下仍非常重要(例如 Spring 利用监听器加载上下文)。

JavaWeb 提供了 8 个监听器接口

监听对象监听器接口(创建/销毁)监听器接口(属性变化)
ServletContextServletContextListenerServletContextAttributeListener
HttpSessionHttpSessionListenerHttpSessionAttributeListener
HttpSessionHttpSessionActivationListener(钝化/活化)HttpSessionBindingListener(绑定)
ServletRequestServletRequestListenerServletRequestAttributeListener

本文重点介绍最常用的 ServletContextListener

Listener基本使用

1 ) 开发步骤

  1. 定义一个类,实现对应的监听器接口(如 ServletContextListener
  2. 重写响应方法(如 contextInitialized()contextDestroyed()
  3. 添加 @WebListener 注解(无需配置路径,自动生效)

2 ) 代码示例

ContextLoaderListener.java

package com.wb.web.listener;
 
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
 
@WebListener 
public class ContextLoaderListener implements ServletContextListener {
    @Override 
    public void contextInitialized(ServletContextEvent sce) {
        // Web应用启动时加载资源 
        System.out.println("ContextLoaderListener...");
    }
 
    @Override 
    public void contextDestroyed(ServletContextEvent sce) {
        // Web应用关闭时释放资源 
    }
}

当应用启动时,控制台会输出 ContextLoaderListener...,表明监听器已自动执行。

ServletContext 对象代表整个Web应用,它的初始化标志着应用加载成功,因此可以在 contextInitialized() 中执行一些全局初始化操作(如加载配置文件、建立数据库连接池等)。

总结

  • Filter(过滤器):拦截请求,实现通用预处理和后处理,通过 @WebFilter 配置拦截路径,通过 chain.doFilter() 放行,多个过滤器形成链式调用,执行顺序由类名的自然排序决定。
  • Listener(监听器):监听域对象的生命周期和属性变化,通过 @WebListener 自动注册,常用于应用启动时的资源加载。
  • 两者配合 Servlet 构成了 JavaWeb 开发的三大基石,理解它们能够帮助我们编写更加优雅、可维护的 Web 应用。

以上便是 JavaWeb 中 FilterListener 的核心知识,通过本文的学习,你已经掌握了过滤器的开发、执行流程、路径配置、过滤器链以及实战登录验证案例,同时也了解了监听器的基本用法。

在实际项目中,这些组件将极大程度地简化重复代码,提升开发效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值