> 文档中心 > JavaWeb——过滤器和监听器

JavaWeb——过滤器和监听器

目录

      • 1. 过滤器
        • 1.1 开发过滤器
        • 1.2 开发过滤器链
        • 1.3 使用过滤器实现汉字编码过滤器
      • 2. 监听器

1. 过滤器

在 JSP 的 Web 应用程序中,过滤器是一种在服务端运行的 Web 组件程序,它可以截取客户端给服务器发的请求,也可以截取服务器给客户端的响应,如图 1 所示。
在这里插入图片描述

 那么过滤器在什么地方使用呢?下面列举了 5 个过滤器使用的场景。 1:在网上的一些评论中经常看到不文明的词汇会被*所代替,这种功能的实现就是在用户提交评论时,评论内容先先经过过滤器,过滤器将不文明词汇替换成*,然后将过滤后的评论内容传递到目标文件。 2:表单提交的中文需要进行请求编码设置,可以通过过滤器统一进行请求编码设置。 3:可以通过过滤器为上传的图片统一添加水印。 4:对客户端请求进行权限认证,可以通过滤器统一进行处理。 5:可以对响应数据进行统一处理。 

当 Web 容器获得一个对资源的请求时,Web 容器判断是否存在过滤器和这个资源关联。如果存在关联就把请求交给过滤器去处理,在过滤器中可以对请求的内容做出改变,然后再将请求转交给被请求的资源。当被请求的资源做出响应时,Web 容器同样会将响应先转发给过滤器,在过滤器中可以对响应做出处理然后再将响应发送给客户端。在这整个过程中客户端和目标资源是不知道过滤器的存在。

在一个 Web 应用程序中可以配置多个过滤器,从而形成过滤器链。在请求资源时,过滤器链中的过滤器依次对请求作出处理。在接受到响应时再按照相反的顺序对响应作出处理,如图 2 所示。
在这里插入图片描述
需要注意的是在过滤器中不一定必须将请求发送给被请求资源,也可以直接给客户端做出响应。

开发过滤器需要以下两个步骤

  1. 定义过滤器类,实现 javax.servlet.Filter 接口
  2. 重写 init()方法、doFilter()方法、destroy()方法
  3. 配置过滤器

1.1 开发过滤器

第一步:创建过滤器类
在 eclipse 中创建一个新的 Dynamic Web Project,命名为 myFilter。在 myFilter 项目中创建包 cn.itlaobing.filter,在该包中创建类 FirstFilter 类,代码如下

package cn.itlaobing.filter; import java.io.IOException; import javax.servlet.*; public class FirstFilter implements javax.servlet.Filter {   public void init(FilterConfig config) throws ServletException {   }   public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {     System.out.println("过滤器 1 请求");     chain.doFilter(request, response);     System.out.println("过滤器 1 响应");   }   public void destroy() {   } } 

代码解析

  1. 过滤器是实现 javax.servlet.Filter 接口的类,并重写 init()、doFilter()、destroy()方法。
  2. Init()方法完成过滤器的初始化工作,由 Web 容器来调用。
  3. destroy()方法用来释放资源,由 Web 容器来调用。
  4. doFilter()方法类似于 Servlet 中的 service()方法,当客户端请求于此过滤器关联的目标对象时,就会调用过滤器的 doFilter()方法。在这个方法中利用 FilterChain 接口中的 doFilter()方法将请求交个下个过滤器来处理,如果该过滤器是过滤器链中的最后一个过滤器,则将请求交给被请求资源,也可以直接给客户端返回响应信息。

第二步:配置过滤器
过滤器类定义后,还需要在 web.xml 中进行过滤器配置,通过配置设置用户访问哪些资源时需要经过该过滤器。
web.xml 配置

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns="http://java.sun.com/xml/ns/javaee"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   id="WebApp_ID" version="3.0">   <filter>     <filter-name>firstFilter</filter-name>     <filter-class>cn.itlaobing.filter.FirstFilter</filter-class>   </filter>   <filter-mapping>     <filter-name>firstFilter</filter-name>     <url-pattern>/*</url-pattern>   </filter-mapping> </web-app> 

代码解析

  1. 节点描述该 Filter 对应的类。
  2. 中的必须和节点中的值相同
  3. 配置过滤器类的存储路径
  4. 指定该过滤器关联的 URL,比如/*指的是对所以资源都过滤,/admin/*指的是对 admin 目录下的所有资源进行过滤。

第三步:测试过滤器
在 myFilter 项目中添加一个 jsp 文件,命名为 test.jsp。然后将 myFilter 项目部署到
tomcat 容器中,启动 tomcat 服务器。访问 test.jsp 文件,观察 eclipse 控制台输出的内容如下
在这里插入图片描述

1.2 开发过滤器链

在之前的基础上再添加一个过滤器,实现过滤器链。
第一步:定义过滤器类
在 myFilter 项目的包 cn.itlaobing.filter 中添加一个类,命名为 SecondFilter,代码如下

package cn.itlaobing.filter; import java.io.IOException; import javax.servlet.*; public class SecondFilter implements javax.servlet.Filter {   public void init(FilterConfig config) throws ServletException {   }   public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {     System.out.println("过滤器 2 请求");//  后续过滤器执行前,先执行该行代码     chain.doFilter(request, response);//  将请求与响应对象向后续过滤器传递     System.out.println("过滤器 2 响应");//  后续过滤器执行后,再执行该行代码   }   public void destroy() {   } } 

第二步:配置过滤器

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns="http://java.sun.com/xml/ns/javaee"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   id="WebApp_ID" version="3.0">      <filter>     <filter-name>firstFilter</filter-name>     <filter-class>cn.itlaobing.filter.FirstFilter</filter-class>   </filter>   <filter-mapping>     <filter-name>firstFilter</filter-name>     <url-pattern>/*</url-pattern>   </filter-mapping>      <filter>     <filter-name>secondFilter</filter-name>     <filter-class>cn.itlaobing.filter.SecondFilter</filter-class>   </filter>   <filter-mapping>     <filter-name> secondFilter</filter-name>     <url-pattern>/*</url-pattern>   </filter-mapping> </web-app> 

第三步:测试过滤器
在浏览器的地址栏中请求 test.jsp,控制台输出结果如图 3 所示:
在这里插入图片描述

通过图 3 的结果可以得知在调用 FilterChain 对象的 doFilter 方法之前的代码都是对请求的过滤,在此之后的都是对响应的过滤。整个过程如下:
在这里插入图片描述

那么怎么来确定一个过滤器在过滤器链中的顺序呢?这个是根据过滤器在 web.xml 中配置的上下顺序来决定的,配置在前面的过滤器先执行,配置在后面的过滤器后执行。

如果要正确的获得表单中的中文或给客户端输出中文那
么就要在 Servlet 中添加如下两行代码:
request.setCharacterEncoding(“UTF-8”);
response.setCharacterEncoding(“UTF-8”);
如果在每个 Servlet 中都添加这样的两行代码,无疑是代码的重复。我们可以将这两行代码放在过滤器中进行处理。

1.3 使用过滤器实现汉字编码过滤器

第一步:定义过滤器类
在 myFilter 项目的包 cn.itlaobing.filter 中添加一个类,命名为 CharacterFilter,代码如下

package cn.itlaobing.filter; import java.io.IOException; import javax.servlet.*; public class CharacterFilter implements Filter {   private String encode = "utf-8";   @Override   //  从 web.xml 配置文件中读取默认编码,如果没有配置编码,默认使用 utf-8 编码   public void init(FilterConfig filterConfig) throws ServletException {     // FilterConfig 用于从 web.xml 中配置读取默认编码     encode = filterConfig.getInitParameter("encode");     if (encode == null) {encode = "UTF-8";     } else {encode = encode.toUpperCase();     }   }   @Override   public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {HttpServletRequest request =(HttpServletRequest) req;     HttpServletResponse response = (HttpServletResponse) res;     if ("POST".equalsIgnoreCase(request.getMethod())) {request.setCharacterEncoding(encode);request.setCharacterEncoding(encode);response.setContentType("text/html;charset=" + encode);chain.doFilter(request, response);return;     }     chain.doFilter(request, response);   }   @Override   public void destroy() {   } } 

代码解析

  1. Init()方法从 web.xml 配置文件中读取编码方式,若配置文件中没有设置编码方式,默认使用 utf-8 编码。
  2. 在 doFilter()方法中判断请求方式若为 POST 方式,则对请求编码和响应编码进行了设置。

第二步:配置过滤器

<!--  配置汉字编码过滤器  --> <filter>   <filter-name>CharacterFilter</filter-name>   <filter-class>cn.itlaobing.filter.CharacterFilter</filter-class> </filter> <filter-mapping>   <filter-name>CharacterFilter</filter-name>   <url-pattern>/*  

第三步:测试过滤器
下面通过表单提交中文来测试过滤器是否对中文提交进行了编码。在 myFilter 项目中添加一个 jsp 文件,命名为 add.jsp,代码如下

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>测试编码过滤器</title> </head> <body> <form action="MyServlet" method="post"> 请输入汉字<input type="text" name="key" value="我是汉字"> <input type="submit" value="发送"> </form> </body> </html> 

在 myFilter 项目中添加一个获取表单数据的 Servlet,命名为 MyServlet,代码如下

package cn.itlaobing.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; @WebServlet("/MyServlet") public class MyServlet extends HttpServlet {   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     doGet(request, response);   }   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {     String key = request.getParameter("key");     System.out.println("key="+key);   } } 

部署项目,在浏览器中打开 add.jsp 文件,点击添加按钮,观察 eclipse 控制台输出的结果,发现汉字显示正常,表明过滤器工作正常。
在这里插入图片描述

2. 监听器

监听器的作用是监听 Web 应用程序中某一个对象,并根据应用程序的需求做出相应的处理,Java Web 应用程序中,Servlet 容器提供了多种监听器,详见表 1:

监听器 描述
ServletRequestListener 监听 request 对象的创建和销毁
ServletRequestAttributeListener 监听向 request 作用域赋值和取值
HttpSessionListener 监听 session 对象的创建和销毁
HttpSessionAttributeListener 监听向 session 作用域赋值和取值
ServletContextListener 监听 application 对象的创建和销毁
ServletContextAttributeListener 监听向 application 作用域赋值和取值

开发过滤器需要以下三个步骤

  1. 定义监听器类,实现监听器接口
  2. 重写相应的方法
  3. 配置监听器

2.1 统计在线人数

下面使用监听器实现在线人数统计功能。思路如下,当有用户访问时,web 容器会创建会话;当有用户退出时,web 容器会销毁会话。HttpSessionListener 监听器能够监听会话的创建和销毁,在会话创建时,在线人数加 1,在会话销毁时,在线人数减 1。在线人数需要存储在变量中,这个变量的值需要能够在每一位访问者的页面上显示,因此该变量存储在 application 作用域中。
在 IDEA 项目中创建一个 Dynamic Web Project,命名为 online,在 online 项目中添加包 cn.itlaobing.listener。

第一步:创建 application 对象监听器
在 cn.itlaobing.listener 包中创建类 ServletContextListenerImpl,实现 javax. servlet. ServletContextListener 接口,用于监听 application 对象的创建和销毁。当 application 创建时(web 容器启动时),设置在线人数为 0,当 application 对象销毁时(web 容器关闭时),清空在线人数。代码如下

package cn.itlaobing.listener; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; public class ServletContextListenerImpl implements ServletContextListener {   public void contextInitialized(ServletContextEvent event) {     int num = 0;     ServletContext application = event.getServletContext();     application.setAttribute("onLineNum", num);   }   public void contextDestroyed(ServletContextEvent event) {     ServletContext application = event.getServletContext();     application.removeAttribute("onLineNum");   } } 

代码解析

  1. ServletContextListener 监听器用于监听 application 对象的创建和销毁
  2. 当 application 创建时,监听器自动调用 contextInitialized()方法
  3. 当 application 对象销毁时,监听器自动调用 contextDestroyed()方法
  4. contextInitialized()方法中的参数 ServletContextEvent 的 getServletContext()方法用于获取被创建的 application 对象,并将在线人数 0 保存到键为 onLineNum 的application 作用域中。
  5. 当 application 对象销毁时,在 contextDestroyed()方法中将键为 onLineNum 的对象从application 作用域中移除。

第二步:配置 application 监听器
在 web.xml 中配置 application 监听器,代码如下

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns="http://java.sun.com/xml/ns/javaee"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   id="WebApp_ID" version="3.0">      <listener>     <listener-class>cn.itlaobing.listener.ServletContextListenerImpl</listener-class>   </listener> </web-app> 

代码解析

  1. 节点配置监听器
  2. 指明监听器类的路径

第三步:创建 session 监听器
在 cn.itlaobing.listener 包中创建类 HttpSessionListenerImpl,代码如下

package cn.itlaobing.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; public class HttpSessionListenerImpl implements HttpSessionListener {   public void sessionCreated(HttpSessionEvent event) {     ServletContext application = event.getSession().getServletContext();     Integer num = (Integer) application.getAttribute("onLineNum");     if (num != null) {int count = num;count = count + 1;application.setAttribute("onLineNum", count);     } else {application.setAttribute("onLineNum", 1);     }   }   public void sessionDestroyed(HttpSessionEvent event) {     ServletContext application = event.getSession().getServletContext();     Integer num = (Integer) application.getAttribute("onLineNum");     int count = num;     count = count - 1;     application.setAttribute("onLineNum", count);   } } 

代码解析

  1. 当 Session 被创建时,会调用监听器的 sessionCreated 方法。
  2. 当 Session 被销毁时,会调用监听器的 sessionDestroyed 方法。
  3. 在 sessionCreated()方法中实现在线人数加 1 的功能。
  4. 在 sessionDestroyed()方法中实现在线人数减 1 的功能。

第四步:配置 session 监听器
在 web.xml 中配置 session 监听器,代码如下

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns="http://java.sun.com/xml/ns/javaee"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"   id="WebApp_ID" version="3.0">      <listener>     <listener-class>cn.itlaobing.listener.ServletContextListenerImpl</listener-class>   </listener>      <listener>     <listener-class>cn.itlaobing.listener.HttpSessionListenerImpl</listener-class>   </listener> </web-app> 

第五步:在界面上显示在线人数
在 online 项目中,添加一个 jsp 文件,命名为 online.jsp,online.jsp 用于显示在线人数,代码如下

<body>当前在线人数:${onLineNum } </body> 

第六步:测试在线人数
将项目部署到 tomcat 容器,启动 tomcat 容器,在浏览器中访问该项目的 online.jsp 文件,查看在线人数。