从Filter到Listener:揭秘Java开发中的神奇力量
今日目标:
能够使用 Filter
能够使用 Listener
1. Filter
1.1 Filter概述
Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
-
Servlet作用:接收请求,发送响应 -
Filter(过滤器)作用: 辅助Servlet,可以对资源的请求进行拦截,从而实现一些特殊的功能,比如如验证、安全性检查、日志记录、数据转换等。 -
Listener(监听器)作用:辅助Servlet,可以对特定时间进行监听,并在事件发生时触发相应的回调方法。常见的监听器包括事件监听器、上下文监听器和会话监听器等
如图所示,浏览器可以是服务器上任意资源(servlet、jsp、html等资源)

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

1.2 Filter快速入门
1.2.1 开发步骤
进行 Filter
开发分成以下三步实现
-
定义类,实现 Filter接口,并重写其所有方法

/**
* 过滤器拦截方法
* @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 代码演示
<?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
。
控制台结果如下

上述效果说明 Demo1Filter
这个过滤器的 doFilter()
方法执行。并且访问到了TestServlet
思考:为什么经过doFilter()方法后可以访问到TestServlet?
这是因为在doFilter()方法中调用了
chain.doFilter(request,response);
//放行
chain.doFilter(request,response);
如果注释掉这行代码,显示结果如下:说明没有放行,也就是进行拦截了

1.3 Filter执行流程

如图是使用过滤器的流程,我们通过以下问题来研究过滤器的执行流程:
-
放行后访问对应资源,资源访问完成后,还会回到Filter中吗?
会回到Filter
-
如果回到Filter中,是重头执行还是执行放行后的逻辑呢?
到放行后逻辑,如果是重头执行的话,就意味着重新执行doFilter方法,然后就会死循环, ,肯定不会这样设计了;所以访问完资源后,会回到
放行后逻辑
,执行该部分代码。
通过上述的说明,我们就可以总结Filter的执行流程如下:

思考:为什么我们编写一个Filter之后就可以产生拦截资源?
因为
@WebFilter("/*")
, 使用的是/*
,表示所有发送到服务器的请求都要经过该过滤器,这就叫做Filter的拦截路径
1.4 Filter拦截路径配置
思考:当浏览器访问目标资源的时候,如果符合拦截条件,但是目标地址在服务器不存在,过滤器会不会运行?
如果请求的URL地址不存在,但是匹配过滤的地址,还是会执行过滤器

拦截路径表示 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链中的每一个过滤器需要特意指定顺序?我们通过一个生活例子给大家说明

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

上图中的过滤器链执行是按照以下流程执行:
-
执行 Filter1
的放行前逻辑代码 -
执行 Filter1
的放行代码 -
执行 Filter2
的放行前逻辑代码 -
执行 Filter2
的放行代码 -
访问到资源 -
执行 Filter2
的放行后逻辑代码 -
执行 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
进行测试,在控制台打印内容如下

从结果可以看到确实是按照我们之前说的执行流程进行执行的。
1.5.3 问题
思考:为什么是先执行 FilterDemo
,后执行 FilterDemo2
呢?
我们现在使用的是注解配置Filter,而这种配置方式的优先级是按照过滤器类名(字符串)的自然排序。
比如有如下两个名称的过滤器 :BFilterDemo
和 AFilterDemo
。那一定是 AFilterDemo
过滤器先执行。
1.5 XML配置Filter
xml配置Filter是在使用注解之前的形式,我们需要了解
步骤:
-
将上面的两个Filter的@WebFilter进行注解

-
配置Filter拦截资源的路径:在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">
<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
,显示结果
发现XML配置的过滤器链,按照
<filter-mapping>
配置的先后顺序
2. Listener
2.1 概述
-
Listener 表示监听器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
-
监听器可以监听就是在
application(应用程序)
,session
,request
三个对象创建、销毁或者往其中添加修改删除属性时自动执行代码的功能组件。request 和 session 我们学习过。而
application
是ServletContext
类型的对象。ServletContext
代表整个web应用,在服务器启动的时候,tomcat会自动创建该对象。在服务器关闭时会自动销毁该对象。
2.2 分类
JavaWeb 提供了8个监听器:
这里面只有 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
对象在服务器启动的时候被创建了。

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

如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!
原文始发于微信公众号(springboot葵花宝典):Web-9-从Filter到Listener:揭秘Java开发中的神奇力量
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/183898.html