Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

从Filter到Listener:揭秘Java开发中的神奇力量

今日目标:

  • 能够使用 Filter

  • 能够使用 Listener

1.  Filter

1.1  Filter概述

Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。

  • Servlet作用:接收请求,发送响应
  • Filter(过滤器)作用: 辅助Servlet,可以对资源的请求进行拦截,从而实现一些特殊的功能,比如如验证、安全性检查、日志记录、数据转换等。
  • Listener(监听器)作用:辅助Servlet,可以对特定时间进行监听,并在事件发生时触发相应的回调方法。常见的监听器包括事件监听器、上下文监听器和会话监听器等

如图所示,浏览器可以是服务器上任意资源(servlet、jsp、html等资源)

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

在访问资源之前,可以使用Filter进行拦截,也就是说在访问资源之前会先经过 Filter,如图所示:

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

1.2  Filter快速入门

1.2.1  开发步骤

进行 Filter 开发分成以下三步实现

  • 定义类,实现 Filter接口,并重写其所有方法
Web-9-从Filter到Listener:揭秘Java开发中的神奇力量
  • 配置Filter拦截资源的路径:在类上定义 @WebFilter 注解。而注解的 value 属性值 /* 表示拦截所有的资源

    Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

  • 在doFilter方法中输出一句话,并放行

    /**
     * 过滤器拦截方法
     * @param request 请求
     * @param response 响应
     * @param chain
     * @throws IOException
     * @throws ServletException
     */

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("demo1Filter 拦截前代码...");
        //放行
        chain.doFilter(request,response);
        System.out.println("demo1Filter 拦截后代码...");

    }

上述代码中的 chain.doFilter(request,response); 就是放行,也就是让其访问本该访问的资源。

1.2.2  代码演示

  • 1.创建一个项目,项目下有一个 TestServlet ,项目结构如下:Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

  • 2.pom.xml 配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <groupId>com.zbbmeta</groupId>  
  <artifactId>09_Filter_Listener</artifactId>  
  <version>1.0-SNAPSHOT</version>  
  <packaging>war</packaging>
  <properties> 
    <maven.compiler.source>11</maven.compiler.source>  
    <maven.compiler.target>11</maven.compiler.target> 
  </properties>

  <dependencies>
<!--    servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

  • 3.TestServlet 内容如下:
package com.zbbmeta.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/test")
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get请求
        System.out.println("你好 test Servlet ...");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post请求
        doGet(request, response);
    }
}
  • 4.Demo1Filter 内容如下:
package com.zbbmeta.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class Demo1Filter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    /**
     * 过滤器拦截方法
     * @param request 请求
     * @param response 响应
     * @param chain
     * @throws IOException
     * @throws ServletException
     */

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("demo1Filter 拦截前代码...");
        //放行
        chain.doFilter(request,response);
        System.out.println("demo1Filter 拦截后代码...");

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

我们现在在浏览器输入 http://localhost:8080/filter-listener/test 访问 TestServlet

控制台结果如下

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

上述效果说明 Demo1Filter 这个过滤器的 doFilter() 方法执行。并且访问到了TestServlet

思考:为什么经过doFilter()方法后可以访问到TestServlet?

这是因为在doFilter()方法中调用了 chain.doFilter(request,response);

//放行
 chain.doFilter(request,response);

如果注释掉这行代码,显示结果如下:说明没有放行,也就是进行拦截了

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

1.3  Filter执行流程

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

如图是使用过滤器的流程,我们通过以下问题来研究过滤器的执行流程:

  • 放行后访问对应资源,资源访问完成后,还会回到Filter中吗?

会回到Filter

  • 如果回到Filter中,是重头执行还是执行放行后的逻辑呢?

到放行后逻辑,如果是重头执行的话,就意味着重新执行doFilter方法,然后就会死循环, ,肯定不会这样设计了;所以访问完资源后,会回到 放行后逻辑,执行该部分代码。

通过上述的说明,我们就可以总结Filter的执行流程如下:

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

思考:为什么我们编写一个Filter之后就可以产生拦截资源?

因为@WebFilter("/*"), 使用的是/*,表示所有发送到服务器的请求都要经过该过滤器,这就叫做Filter的拦截路径

1.4  Filter拦截路径配置

思考:当浏览器访问目标资源的时候,如果符合拦截条件,但是目标地址在服务器不存在,过滤器会不会运行?

如果请求的URL地址不存在,但是匹配过滤的地址,还是会执行过滤器

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

拦截路径表示 Filter 会对请求的哪些资源进行拦截,使用 @WebFilter 注解进行配置。如:@WebFilter("拦截路径")

Filter 可以根据需求,配置不同的拦截资源路径。

拦截路径有四种配置方式:

  • 拦截具体的资源:/index.jsp:只有访问index.jsp时才会被拦截
  • 目录拦截:/role/*:访问/role下的所有资源,都会被拦截
  • 后缀名拦截:*.jsp:访问后缀名为jsp的资源,都会被拦截
  • 拦截所有:/*:访问所有资源,都会被拦截
//@WebFilter(value = "/index.jsp")
//@WebFilter(value = "/role/*")
//@WebFilter(value = "*.jsp")
@WebFilter(value = "/*")
public class Demo1Filter implements Filter

通过上面拦截路径的学习,大家会发现拦截路径的配置方式和 Servlet 的请求资源路径配置方式一样,但是表示的含义不同。

1.5  过滤器链

1.5.1  概述

过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。思考:Filter链中的每一个过滤器需要特意指定顺序?我们通过一个生活例子给大家说明

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

张三同学要去教室上课,但是因为特殊原因,需要对没有同学进行健康码检查和扫码登记,现在大家想一下,健康码检查和扫码登记需要特意指定顺序?是不是不需要,只要最终结果是两个都执行,,才能放行去教室上课,只要有任意一个不符合,就不能去教室上课。

如下图就是一个过滤器链,我们学习过滤器链主要是学习过滤器链执行的流程,不需要特意考虑执行顺序

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

上图中的过滤器链执行是按照以下流程执行:

  1. 执行 Filter1 的放行前逻辑代码
  2. 执行 Filter1 的放行代码
  3. 执行 Filter2 的放行前逻辑代码
  4. 执行 Filter2 的放行代码
  5. 访问到资源
  6. 执行 Filter2 的放行后逻辑代码
  7. 执行 Filter1 的放行后逻辑代码

以上流程串起来就像一条链子,故称之为过滤器链。

1.5.2  代码演示

  • 编写第一个过滤器 Demo1Filter ,配置成拦截所有资源
package com.zbbmeta.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class Demo1Filter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    /**
     * 过滤器拦截方法
     * @param request 请求
     * @param response 响应
     * @param chain
     * @throws IOException
     * @throws ServletException
     */

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("demo1Filter 拦截前代码...");
        //放行
        chain.doFilter(request,response);
        System.out.println("demo1Filter 拦截后代码...");

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
  • 编写第二个过滤器 Demo2Filter ,配置成拦截所有资源
package com.zbbmeta.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class Demo2Filter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    /**
     * 过滤器拦截方法
     * @param request 请求
     * @param response 响应
     * @param chain
     * @throws IOException
     * @throws ServletException
     */

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("demo2Filter 拦截前代码...");
        //放行
        chain.doFilter(request,response);
        System.out.println("demo2Filter 拦截后代码...");

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

  • TestServlet输出语句到控制台
package com.zbbmeta.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/test")
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get请求
        System.out.println("你好 test Servlet ...");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post请求
        doGet(request, response);
    }
}

  • 启动服务器,在浏览器输入 http://localhost:8080/filter-listener/test 进行测试,在控制台打印内容如下
Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

从结果可以看到确实是按照我们之前说的执行流程进行执行的。

1.5.3  问题

思考:为什么是先执行 FilterDemo ,后执行 FilterDemo2 呢?

我们现在使用的是注解配置Filter,而这种配置方式的优先级是按照过滤器类名(字符串)的自然排序。

比如有如下两个名称的过滤器 :BFilterDemoAFilterDemo 。那一定是 AFilterDemo 过滤器先执行。

1.5  XML配置Filter

xml配置Filter是在使用注解之前的形式,我们需要了解 

步骤:

  • 将上面的两个Filter的@WebFilter进行注解
Web-9-从Filter到Listener:揭秘Java开发中的神奇力量
  • 配置Filter拦截资源的路径:在web.xml中配置
Web-9-从Filter到Listener:揭秘Java开发中的神奇力量
<?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">


 <filter>
  <filter-name>Demo1Filter</filter-name>
  <filter-class>com.zbbmeta.filter.Demo1Filter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>Demo1Filter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

</web-app>
  • 浏览器输入地址http://localhost:8080/filter-listener/test,显示结果

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量思考:XML配置的过滤器链,执行的先后顺序是什么?

发现XML配置的过滤器链,按照<filter-mapping>配置的先后顺序

2. Listener

2.1  概述

  • Listener 表示监听器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。

  • 监听器可以监听就是在 application(应用程序)sessionrequest 三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。

    request 和 session 我们学习过。而 applicationServletContext 类型的对象。

    ServletContext 代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。

2.2  分类

JavaWeb 提供了8个监听器:

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

这里面只有 ServletContextListener 这个监听器后期我们会接触到,ServletContextListener 是用来监听 ServletContext 对象的创建和销毁。

ServletContextListener 接口中有以下两个方法

  • void contextInitialized(ServletContextEvent sce)ServletContext 对象被创建了会自动执行的方法
  • void contextDestroyed(ServletContextEvent sce)ServletContext 对象被销毁时会自动执行的方法

2.3  代码演示

我们只演示一下 ServletContextListener 监听器

  • 定义一个类,实现ServletContextListener 接口
  • 重写所有的抽象方法
  • 使用 @WebListener 进行配置

代码如下:

package com.zbbmeta.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        System.out.println("Web应用程序启动");
    }

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        System.out.println("Web应用程序关闭");
    }
}

启动服务器,就可以在启动的日志信息中看到 contextInitialized() 方法输出的内容,同时也说明了 ServletContext 对象在服务器启动的时候被创建了。

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

关闭程序:说明程序关闭的时候调用了contextDestroyed方法

Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!

原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!


原文始发于微信公众号(springboot葵花宝典):Web-9-从Filter到Listener:揭秘Java开发中的神奇力量

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/183898.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之家——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!