当前位置: 首页 > news >正文

过滤器和监听器

一、过滤器 Filter
Filter 是 JavaWeb 三大组件之一,核心作用是拦截请求,而非处理请求,与 Servlet 配合实现请求的预处理、权限校验等功能。

1. 过滤器核心概念
用户请求目标 Servlet 时,会先执行绑定该请求的 Filter,Filter 决定是否 “放行”,放行则执行目标 Servlet,不放行则终止请求。
执行完 Servlet 的代码后,会回溯执行 Filter 中放行代码后的逻辑,请求并非给客户端输出后就结束。
一个目标资源可绑定多个 Filter,执行遵循 “先进先执行、后进先回溯” 规则,即多个过滤器执行顺序为 1 前→2 前→目标资源→2 后→1 后。
2. 过滤器创建与配置
创建步骤:定义 Java 类实现javax.servlet.Filter接口,并重写init()、doFilter()、destroy()三个核心方法。
配置方式:在 web.xml 中通过<filter>和<filter-mapping>标签配置,也可使用注解(与 Servlet 注解方式类似)。
路径匹配规则:与 Servlet 的url-pattern完全一致,分为完全路径匹配(/aaa)、目录匹配(/aaa/、/)、扩展名匹配(*.do)三种。
3. 过滤器生命周期
init(FilterConfig):服务器启动时创建 Filter 实例,每个类型的 Filter 仅创建一个,创建后立即调用该方法完成初始化,仅执行一次。
doFilter(ServletRequest, ServletResponse, FilterChain):用户每次访问目标资源时执行,是过滤器的核心方法;需调用FilterChain.doFilter()方法才会放行,否则目标资源无法执行。
destroy():Filter 实例创建后存入服务器缓存持续使用,服务器关闭时销毁实例,销毁前调用该方法,仅执行一次。
4. FilterConfig
与 ServletConfig 功能相似,对应 web.xml 中 Filter 的配置信息,是init()方法的参数,无法自行创建。
核心方法:
ServletContext getServletContext():获取当前 Web 应用的 ServletContext 对象。
String getFilterName():获取<filter-name>配置的过滤器名称。
String getInitParameter(String name):根据参数名获取 Filter 的初始化参数值。
Enumeration getInitParameterNames():获取 Filter 所有初始化参数的名称,可遍历获取所有参数。
配置方式:在 web.xml 的<filter>标签中添加<init-param>子元素,包含<param-name>和<param-value>,可配置多个。
5. FilterChain 过滤器链
是doFilter()方法的参数,仅包含doFilter(ServletRequest, ServletResponse)一个方法,核心作用是实现放行逻辑。
放行的本质:调用该方法并非直接执行目标资源,而是执行下一个过滤器的doFilter()方法;若当前过滤器是最后一个,则执行目标资源。
代码执行规则:FilterChain.doFilter()方法之前的代码,在目标资源执行前执行;之后的代码,在目标资源执行后执行。
6. 过滤器执行顺序
单个目标资源绑定多个过滤器时,执行顺序由web.xml 中的部署顺序决定。
部署规则:<filter>和<filter-mapping>标签的书写顺序即为过滤器的执行顺序,先配置的过滤器先执行,后配置的后执行。
回溯规则:目标资源执行完成后,过滤器按执行顺序的倒序执行放行后的代码,即最后执行的过滤器先回溯。
7. 过滤器目标资源设置
通过url-pattern匹配:如/*拦截所有请求,/admin/*拦截管理员路径下的所有敏感资源,与 Servlet 路径配置一致。
通过 Servlet 名称匹配:在<filter-mapping>中使用<servlet-name>标签,指定拦截某个具体的 Servlet,当访问该 Servlet 时触发过滤器。
8. 过滤器核心应用场景
预处理请求:如设置请求和响应的编码格式,解决中文乱码问题,此类场景一般都会放行,仅做前置准备工作。
权限校验:如判断用户是否登录、用户 IP 是否被禁用、是否拥有访问资源的权限,不满足条件则拒绝放行。
后置处理:在目标资源执行后,对响应的数据进行加工处理,如统一处理返回结果、添加响应头信息。
9. 过滤器经典应用:解决 POST 中文乱码
配置:在 web.xml 中配置过滤器,设置初始化参数encode为 UTF-8,url-pattern为/*拦截所有请求。
核心代码:在doFilter()方法中,通过request.setCharacterEncoding(encode)设置请求编码,通过response.setCharacterEncoding(encode)和response.setContentType("text/html;charSet="+encode)设置响应编码,最后调用放行方法。
优势:一次配置,全局生效,无需在每个 Servlet 中单独设置编码,简化开发。

package cn.tx.LearnFilter; import javax.servlet.*; import java.io.IOException; import java.util.Enumeration; import java.util.logging.LogRecord; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { String filterName=filterConfig.getFilterName(); String company=filterConfig.getInitParameter("company"); System.out.println(company); Enumeration<String> names=filterConfig.getInitParameterNames(); while(names.hasMoreElements()){ String name=names.nextElement(); String value=filterConfig.getInitParameter(name); } } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Hello Filter 00!!!"); //放行 filterChain.doFilter(servletRequest,servletResponse); System.out.println("过滤00完成"); } @Override public void destroy() { } }
package cn.tx.LearnFilter; import javax.servlet.*; import java.io.IOException; public class MyFilter01 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("Hello Servlet 01"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("过滤器01完成"); } @Override public void destroy() { } }

二、监听器 Listener
Listener 是 JavaWeb 三大组件之一,基于观察者模式实现,核心作用是监听 Web 应用中域对象的状态变化,当事件触发时自动执行对应的处理方法。

1. 监听器核心概述
监听的事件源:JavaWeb 中的三大域对象,即ServletContext(应用域)、HttpSession(会话域)、ServletRequest(请求域)。
监听的事件类型:域对象的创建与销毁、域对象中属性的添加 / 修改 / 删除、HttpSession的特殊状态变化(如钝化、活化)。
核心作用:感知域对象的状态变化,在事件触发时执行自定义逻辑,如统计在线人数、初始化应用全局数据、监听用户会话状态。
2. 常用核心监听器
(1)ServletContextListener:监听 ServletContext 对象
监听应用的启动与关闭,ServletContext在服务器启动时创建,服务器关闭时销毁。
核心方法:
contextInitialized(ServletContextEvent evt):ServletContext 对象创建后调用,可用于初始化应用全局数据(如加载配置文件、初始化全局变量)。
contextDestroyed(ServletContextEvent evt):ServletContext 对象销毁前调用,可用于释放应用资源(如关闭数据库连接)。
(2)HttpSessionListener:监听 HttpSession 对象
监听用户会话的创建与销毁,HttpSession在用户第一次访问应用时创建,会话超时 / 手动销毁时销毁。
核心方法:
sessionCreated(HttpSessionEvent evt):HttpSession 对象创建后调用,可用于统计在线人数、记录用户登录信息。
sessionDestroyed(HttpSessionEvent evt):HttpSession 对象销毁前调用,可用于统计用户下线、清理会话数据。
(3)ServletRequestListener:监听 ServletRequest 对象
监听请求的创建与销毁,ServletRequest在用户每次发起请求时创建,请求响应完成后销毁。
核心方法:
requestInitialized(ServletRequestEvent sre):ServletRequest 对象创建后调用,可用于记录请求日志、获取请求信息。
requestDestroyed(ServletRequestEvent sre):ServletRequest 对象销毁前调用,可用于清理请求相关资源。
3. 监听器的使用步骤
定义实现类:创建 Java 类,实现对应的监听器接口(可同时实现多个监听器接口,如同时实现 ServletContextListener 和 HttpSessionListener)。
重写方法:根据实现的接口,重写对应的事件处理方法,编写自定义业务逻辑。
配置注册:在 web.xml 中通过<listener>标签配置,指定监听器的全类名,服务器启动时会自动加载并注册。
4. 监听器经典应用:实现在线人数实时统计
核心思路:通过HttpSessionListener监听会话的创建与销毁,会话创建则在线人数 + 1,会话销毁则在线人数 - 1;通过ServletContextListener初始化在线人数为 0,并将人数存入ServletContext(全局共享)。
核心步骤:
监听器实现ServletContextListener和HttpSessionListener接口,定义在线人数变量,默认值为 0。
contextInitialized方法:将在线人数初始值存入 ServletContext,实现全局共享。
sessionCreated方法:从 ServletContext 获取当前人数,自增后重新存入,实现人数 + 1。
sessionDestroyed方法:从 ServletContext 获取当前人数,自减后重新存入,实现人数 - 1。
页面展示:在 JSP 页面中通过 EL 表达式${count}获取 ServletContext 中的在线人数,实现实时展示。
会话销毁:通过request.getSession().invalidate()手动销毁会话,模拟用户退出,触发人数 - 1。

package cn.tx.LearnListener; import javax.servlet.*; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; /** * 监听域对象,ServletContext,HttpSession,ServletRequest */ public class MyListener implements ServletContextListener, HttpSessionListener, ServletRequestListener { public MyListener() { } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("requestInitialized"); } @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("requestDestroyed"); } public void contextInitialized(ServletContextEvent sce) { System.out.println("contextInitialized"); } public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed"); } public void sessionCreated(HttpSessionEvent se) { System.out.println("sessionCreated"); } public void sessionDestroyed(HttpSessionEvent se) { System.out.println("sessionDestroyed"); } }
package cn.tx.LearnListener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class MyListener01 implements ServletContextListener, HttpSessionListener { // 定义一个在线人数 初始为0 private Integer count = 0; public MyListener01() { } public void contextInitialized(ServletContextEvent sce) { // 初始化Servlet容器时把在线人数存入 ServletContext context = sce.getServletContext(); context.setAttribute("count",count); System.out.println("contextInitialized"); } public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed"); } public void sessionCreated(HttpSessionEvent se) { // 如果创建了一个session 那么在线人数加1 onLineCount(se.getSession().getServletContext(),true); System.out.println("sessionCreated"); } public void sessionDestroyed(HttpSessionEvent se) { // 如果销毁了一个session 那么在线人数减1 onLineCount(se.getSession().getServletContext(),false); System.out.println("sessionDestroyed"); } /** * 添加或删除在线人数 * @param context * @param isAdd */ private void onLineCount(ServletContext context,boolean isAdd){ if(isAdd){ count = (Integer)context.getAttribute("count"); context.setAttribute("count",++count); }else{ count = (Integer)context.getAttribute("count"); context.setAttribute("count",--count); } } }

配置好的web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 让自己的servlet生效--> <!-- <context-param>--> <!-- <param-name>company</param-name>--> <!-- <param-value>qcby</param-value>--> <!-- </context-param>--> <!-- <servlet>--> <!-- <servlet-name>hello</servlet-name>--> <!-- <servlet-class>cn.tx.servlet.MyServlet</servlet-class>--> <!-- <init-param>--> <!-- <param-name>username</param-name>--> <!-- <param-value>root</param-value>--> <!-- </init-param>--> <!-- <init-param>--> <!-- <param-name>password</param-name>--> <!-- <param-value>12445</param-value>--> <!-- </init-param>--> <!-- <load-on-startup>1</load-on-startup>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>hello</servlet-name>--> <!-- <url-pattern>/hello/*</url-pattern>--> <!-- </servlet-mapping>--> <!-- <servlet>--> <!-- <servlet-name>hello2</servlet-name>--> <!-- <servlet-class>cn.tx.servlet.MyServlet01</servlet-class>--> <!-- <load-on-startup>2</load-on-startup>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>hello2</servlet-name>--> <!-- <url-pattern>*.do</url-pattern>--> <!-- </servlet-mapping>--> <!-- <servlet>--> <!-- <servlet-name>hello3</servlet-name>--> <!-- <servlet-class>cn.tx.servlet.Servlet3</servlet-class>--> <!-- <load-on-startup>3</load-on-startup>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>hello3</servlet-name>--> <!-- <url-pattern>/hello3</url-pattern>--> <!-- </servlet-mapping>--> <!-- 配置过滤器00--> <filter> <filter-name>firstFilter</filter-name> <filter-class>cn.tx.LearnFilter.MyFilter</filter-class> <init-param> <param-name>encode</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>company</param-name> <param-value>txjava</param-value> </init-param> </filter> <filter-mapping> <filter-name>firstFilter</filter-name> <url-pattern>/hello</url-pattern> </filter-mapping> <!-- 过滤器01--> <!-- <filter>--> <!-- <filter-name>secondFilter</filter-name>--> <!-- <filter-class>cn.tx.LearnFilter.MyFilter01</filter-class>--> <!-- </filter>--> <!-- <filter-mapping>--> <!-- <filter-name>secondFilter</filter-name>--> <!-- <url-pattern>/*</url-pattern>--> <!-- </filter-mapping>--> <!-- 登录的servlet--> <!-- <servlet>--> <!-- <servlet-name>login01</servlet-name>--> <!-- <servlet-class>cn.tx.servlet.LoginServlet</servlet-class>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>login01</servlet-name>--> <!-- <url-pattern>/login</url-pattern>--> <!-- </servlet-mapping>--> <!-- <listener>--> <!-- <listener-class>cn.tx.LearnListener.MyListener</listener-class>--> <!-- </listener>--> <!--请求对象--> <!-- <servlet>--> <!-- <servlet-name>response01</servlet-name>--> <!-- <servlet-class>cn.tx.LearnResponse.Response01</servlet-class>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>response01</servlet-name>--> <!-- <url-pattern>/rep00</url-pattern>--> <!-- </servlet-mapping>--> <!-- <servlet>--> <!-- <servlet-name>response02</servlet-name>--> <!-- <servlet-class>cn.tx.LearnResponse.Response02</servlet-class>--> <!-- </servlet>--> <!-- <servlet-mapping>--> <!-- <servlet-name>response02</servlet-name>--> <!-- <url-pattern>/rep01</url-pattern>--> <!-- </servlet-mapping>--> <servlet> <servlet-name>response03</servlet-name> <servlet-class>cn.tx.LearnResponse.Response03</servlet-class> </servlet> <servlet-mapping> <servlet-name>response03</servlet-name> <url-pattern>/response03</url-pattern> </servlet-mapping> <servlet> <servlet-name>response04</servlet-name> <servlet-class>cn.tx.LearnResponse.Response04</servlet-class> </servlet> <servlet-mapping> <servlet-name>response04</servlet-name> <url-pattern>/response04</url-pattern> </servlet-mapping> </web-app>
http://www.jsqmd.com/news/527832/

相关文章:

  • 老旧设备复活计划:使用OpenCore Legacy Patcher实现旧Mac系统升级
  • slowAES嵌入式AES解密库:绕过JS反爬的轻量实现
  • PREi:ESP32/ESP8266轻量级伪REST接口框架
  • RK3588上跑iperf3测速前,你的RTL8188eus USB WiFi驱动真的装对了吗?避坑指南
  • DeepSeek-OCR · 万象识界效果展示:多栏报纸扫描件→逻辑顺序Markdown重排成果
  • thinkphp5模型的基本和高级用法(提供代码示例)
  • 用MATLAB/Simulink手把手搭建汽车悬架模型:从随机路面到舒适性分析(附脚本)
  • 我用Claude Code做了一个TTS的文本转语音工作台(免费、已开源)(Claude Code保姆级图文配置+使用教程+中转站)(MiMo-V2-TTS教程)
  • LumiPixel Canvas Quest人像修复与高清化实战:让老照片焕发新生
  • 百度千帆开源 Qianfan-OCR:端到端文档智能模型的架构革命
  • 创新项目实训博客(二):Flutter 跨平台架构初始化与基建落地
  • C++/Qt使用Snap7对西门子PLC 读写操作
  • 别再让标签打架了!高德地图上车辆标签重叠的3种优雅解决方案(附Vue代码)
  • **数据库技术基础**章节中关于**SQL(结构化查询语言)**的核心知识点,主要聚焦于**字符串模式匹配**和**视图查询
  • ChatGPTuino:ESP32/Arduino轻量级LLM嵌入式客户端
  • 图像融合技术:小波变换与拉普拉斯金字塔方法
  • 免费商用地图哪里找?用QGIS+HCMGIS插件搞定建筑轮廓/路网数据下载
  • Swig实战指南:从零构建Java与C/C++的跨语言桥梁(CMake集成版)
  • 大厂都在找场景,滴滴先把 AI 装进了出行里
  • DeOldify移动端适配初探:在Android设备上实现本地图片上色功能
  • 平面设计师效率工具:RMBG-2.0背景移除镜像实战,复杂场景轻松处理
  • 通义千问1.5-1.8B-Chat-GPTQ-Int4实战:辅助C语言初学者理解指针与内存
  • 《深度研究:提示工程架构师在Agentic AI上下文工程用户体验设计的创新实践》
  • AI Infinity镜像大赛圆满收官!17 款优质镜像上线,共筑国产算力开发者新生态
  • 2026最权威AI论文写作软件排名:这些工具被高校和导师悄悄推荐
  • 5大步骤让老款Mac重获新生:OpenCore Legacy Patcher系统升级全指南
  • GLM-4-9B-Chat-1M详细步骤:vLLM启用max_num_batched_tokens=8192吞吐优化
  • Opera 2026年的最近更新后发布个 Web 30 年回顾
  • Docker容器化离线部署Jitsi-Meet:从镜像打包到内网启动全解析
  • 从价格战到价值战:蚂蚁保定期寿险调价背后的市场新周期