本文纲要
Filter概述Filter快速入门
开发步骤
项目结构
核心代码Filter执行流程
流程图解
代码演示
数据处理时机Filter拦截路径配置
四种配置方式
示例演示- 过滤器链
概念与执行流程
优先级规则
示例代码与输出 - 案例:登录验证过滤器
需求分析
项目结构
登录验证流程
完整代码解析 Listener概述Listener基本使用
开发步骤
代码示例- 总结
Filter概述
在JavaWeb中,有三大核心组件:Servlet、Filter 和 Listener。
Filter(过滤器) 的作用是拦截对Web资源的请求,并在请求到达目标资源前或响应返回客户端前进行一些通用的、重复性的处理。
常见应用场景包括:
- 登录验证:统一判断用户是否已登录
- 权限控制:细粒度访问权限校验
- 统一编码处理:避免每个Servlet重复设置编码
- 敏感词过滤:对请求参数或响应内容进行过滤
过滤器的工作模型如下:
凡是访问Web应用中的资源(Servlet、JSP、HTML等),都可以先经过过滤器,再决定是否放行。
Filter快速入门
1 ) 开发步骤
- 定义一个类,实现
javax.servlet.Filter接口 - 重写三个方法:
init()、doFilter()、destroy()init():初始化时调用doFilter():每次拦截请求时执行,是过滤器的核心destroy():销毁时调用
- 使用
@WebFilter注解配置拦截路径(注意:这里的路径是拦截资源的路径,不是过滤器的访问路径) - 在
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执行流程
过滤器的执行流程可以概括为:放行前逻辑 → 放行 → 访问资源 → 放行后逻辑。
代码演示
在过滤器 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应用可以配置多个过滤器,它们按照一定顺序形成一个过滤器链。请求会依次经过每个过滤器,直到最后一个过滤器放行后到达目标资源,返回时再按相反顺序执行放行后的逻辑。
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 ) 登录验证流程
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 三大组件之一,用于监听 ServletContext、HttpSession、ServletRequest 等域对象的创建、销毁以及属性变化。当监听到指定事件时会自动执行相应方法。
虽然现在监听器使用频率不如 Filter 和 Servlet 高,但在框架初始化等场景下仍非常重要(例如 Spring 利用监听器加载上下文)。
JavaWeb 提供了 8 个监听器接口:
| 监听对象 | 监听器接口(创建/销毁) | 监听器接口(属性变化) |
|---|---|---|
| ServletContext | ServletContextListener | ServletContextAttributeListener |
| HttpSession | HttpSessionListener | HttpSessionAttributeListener |
| HttpSession | HttpSessionActivationListener(钝化/活化) | HttpSessionBindingListener(绑定) |
| ServletRequest | ServletRequestListener | ServletRequestAttributeListener |
本文重点介绍最常用的 ServletContextListener
Listener基本使用
1 ) 开发步骤
- 定义一个类,实现对应的监听器接口(如
ServletContextListener) - 重写响应方法(如
contextInitialized()、contextDestroyed()) - 添加
@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 中 Filter 与 Listener 的核心知识,通过本文的学习,你已经掌握了过滤器的开发、执行流程、路径配置、过滤器链以及实战登录验证案例,同时也了解了监听器的基本用法。
在实际项目中,这些组件将极大程度地简化重复代码,提升开发效率。
238

被折叠的 条评论
为什么被折叠?



