JavaWeb——过滤器和监听器

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 配置

<?xml version="1.0" encoding="UTF-8"?> 
<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() { 
  } 
} 

第二步:配置过滤器

<?xml version="1.0" encoding="UTF-8"?> 
<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>/*</url-pattern> 
</filter-mapping> 

第三步:测试过滤器
下面通过表单提交中文来测试过滤器是否对中文提交进行了编码。在 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 监听器,代码如下

<?xml version="1.0" encoding="UTF-8"?> 
<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"> 
  <!--  配置 application 监听器  --> 
  <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 监听器,代码如下

<?xml version="1.0" encoding="UTF-8"?> 
<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"> 
  <!--  配置 application 监听器  --> 
  <listener> 
    <listener-class>cn.itlaobing.listener.ServletContextListenerImpl</listener-class> 
  </listener> 
  <!--  配置 session 监听器  --> 
  <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 文件,查看在线人数。

  • 3
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Geek Li

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

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

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

打赏作者

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

抵扣说明:

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

余额充值