Filter过滤器

每博一文案

师傅说:人生无坦途,累是必须的背负,看多了,人情人暖,走遍了离合聚散,有时会
在心里对自己说,我想,我是真的累了,小时候有读不完的书,长大后有赚不尽的力。
白天在外要奋斗打拼,把心事都藏起来,笑脸相迎,做一个合格的员工,晚上回家要照顾家人。
把家务都打理的井井有条,做一个称职的伴侣,习惯了所有事情,自己扛,习惯了所有委屈自己消化,
有时候莫名的低落,什么话都不想说,只想一个静静的发呆,有时会突然的烦躁,什么事都不想做,
只想让自己好好的放松,偶尔也会向往过一份属于自己的生活。
没有那么多责任,要背负只做自己想做的事,累了就停下类休息吧,烦了就给自己放个假吧。
这个世上没有铁打的身体,该休息时就得休息。
这个世上没有坚强的心灵,该哭泣时就该哭泣。
看看碧海蓝天,听听轻歌曼舞,会会知己老友,品品清茶,美酒,生活本就可以多姿多彩。
人生说到底,活的是心气,为累过,方知闲,为苦过,方知甜,随缘自在,勿忘心安,便是活着的最美状态。
——————《一禅心灵庙语1》

@

1. Filter 过滤器的概述

在一个比较复杂的Web应用程序中,通常都有很多URL映射,对应的,也会有多个Servlet来处理URL。

我们考察这样一个论坛应用程序:

            ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
/ ┌──────────────┐
│ ┌─────────────>│ IndexServlet │ │
│ └──────────────┘
│ │/signin ┌──────────────┐ │
├─────────────>│SignInServlet │
│ │ └──────────────┘ │
│/signout ┌──────────────┐
┌───────┐ │ ├─────────────>│SignOutServlet│ │
│Browser├─────┤ └──────────────┘
└───────┘ │ │/user/profile ┌──────────────┐ │
├─────────────>│ProfileServlet│
│ │ └──────────────┘ │
│/user/post ┌──────────────┐
│ ├─────────────>│ PostServlet │ │
│ └──────────────┘
│ │/user/reply ┌──────────────┐ │
└─────────────>│ ReplyServlet │
│ └──────────────┘ │
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─

各个Servlet设计功能如下:

  • IndexServlet:浏览帖子;
  • SignInServlet:登录;
  • SignOutServlet:登出;
  • ProfileServlet:修改用户资料;
  • PostServlet:发帖;
  • ReplyServlet:回复。

其中,ProfileServlet、PostServlet和ReplyServlet都需要用户登录后才能操作,否则,应当直接跳转到登录页面。

我们可以直接把判断登录的逻辑写到这3个Servlet中,但是,同样的逻辑重复3次没有必要,并且,如果后续继续加Servlet并且也需要验证登录时,还需要继续重复这个检查逻辑。

为了把一些公用逻辑从各个Servlet中抽离出来,JavaEE的Servlet规范还提供了一种Filter组件,即过滤器,它的作用是,在HTTP请求到达Servlet之前,可以被一个或多个Filter预处理,类似打印日志、登录检查等逻辑,完全可以放到Filter中。

什么是 Filter 过滤器:

  1. Filter 过滤器它是 JavaWeb 的三大组件之一。

    三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器
  2. Filter 过滤器它是 JavaEE 的规范。也就是接口
  3. Filter 过滤器它的作用是:拦截请求,过滤响应。

拦截请求常见的应用场景有:

1.权限检查 2.日记操作 3.事务管理 ……等等

一般情况下,都是在过滤器当中编写公共代码。提高代码的复用性.

2. Filter 过滤器的编写

  • 第一步:编写一个Java类实现一个接口:jarkata.servlet.Filter。并且实现这个接口当中所有的方法。

  • init( )方法:在Filter对象第一次被创建之后调用,并且只调用一次。与Servlet中的init()方法类似,filter中的init()方法用于初始化过滤器。开发者可以在init()方法中完成与构造方法类似的初始化功能。如果初始化代码中要用到FilterConfig对象,则这些初始化代码只能在filter的init()方法中编写,而不能在构造方法中编写。 default 是接口中的一个默认方法,基于 jdk8 新特性,默认方法可以不用重写,如果有需要也是可以重写的.
public default void init(FilterConfig filterConfig) throws ServletException {}
  • doFilter( )方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。需要注意的是,服务器响应的时候,该方法也是会被执行的。doFilter方法类似于Servlet接口的service()方法。当客户端请求目标资源时,容器会筛选出符合<filter-mapping>标签中<url-pattern>的filter,并按照声明<filter-mapping>的顺序依次调用这些filter的doFilter()方法。doFilter()方法有多个参数,其中参数requestresponse为Web服务器或filter链中的上一个filter传递过来的请求和响应对象。参数chain代表当前filter链的对象,只有当前filter对象中的doFilter()方法内部需要调用FilterChain对象的doFilter方法时,才能把请求交付给filter链中的下一个filter或目标程序处理。这个是抽象方法,必须重写。
 public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
  • destroy( )方法:在Filter对象被释放/销毁之前调用,并且只调用一次。 filter中的destroy()方法Servlet中的destroy()作用类似,在Web服务器卸载filter对象之前被调用,用于释放被filter对象打开的资源。 default 是接口中的一个默认方法,基于 jdk8 新特性,默认方法可以不用重写,如果有需要也是可以重写的.
 public default void destroy() {}
package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import java.io.IOException; public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TestFilter 中的 init() 方法 初始化 执行了");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("TestFilter 中的 doFilter() 方法执行了");
} @Override
public void destroy() {
System.out.println("TestFilter 中的 destroy() 方法 销毁 执行了");
}
}
  • 第二步:在web.xml文件中对 Filter进行配置。这个配置和 Servlet很像。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"> <filter>
<!-- 两个 name 名是要保持一致的-->
<filter-name>filter</filter-name>
<!-- 对应的全类路径名,全类限定名-->
<filter-class>com.RainbowSea.filter.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<!-- Filter 的映射路径,以 / 开始-->
<url-pattern>/a.do</url-pattern>
</filter-mapping> </web-app>

如下是关于: Filter的所有的 web.xml 中的配置属性信息:

标签 作用
<filter> 指定一个过滤器
<filter-name> 用于为过滤器指定一个名称,该元素的内容不能为空
<filter-class> 用于指定过滤器的完整的限定类名
<init-param> 用于为过滤器指定初始化参数
<param-value> <init-param>的子参数,用于指定参数的名称
<filter-mapping> 用于设置一个filter所负责拦截的资源
<filter-name> <filter-mapping>子元素,用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名称
<url-pattern> 用于设置filter所拦截的请求路径
<servlet-name> 用于指定过滤器所拦截的Servlet名称

执行效果:

如果Servlet版本大于3.0,也可以使用注解 @WebFilter()的方式来配置filter。

如下:

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/a.do")
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("TestFilter 中的 init() 方法 初始化 执行了");
} @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("TestFilter 中的 doFilter() 方法执行了");
} @Override
public void destroy() {
System.out.println("TestFilter 中的 destroy() 方法 销毁 执行了");
}
}

如下是关于:@WebFilter() 的属性的说明:

属性 类型 是否必须 说明
asyncSupported boolean 指定filter是否支持异步模式
dispatcherTypes DispatcherType[] 指定filter对哪种方式的请求进行过滤
filterName String filter名称
initParams WebInitParam[] 配置参数
displayName String filter显示名
servletNames String[] 指定对哪些Servlet进行过滤
urlPatterns/value String[] 两个属性作用相同,指定拦截的路径

web.xml可以配置的filter属性都可以通过@WebServlet的方式进行配置。不过一般不推荐使用注解方式来配置filter,因为如果存在多个过滤器,使用web.xml配置filter可以控制过滤器的执行顺序,如果使用了注解方式,则不可以这样做了。该 Filter 执行顺序该文章的后面会详细说明,所以请大家,耐心阅读。谢谢。

3. Filter 过滤器的执行过程解析

当用户向服务器发送request请求时,服务器接受该请求,并将请求发送到第一个过滤器中进行处理。如果有多个过滤器,则会依次经过filter2,filter3,…,filter n。接着调用Servlet中的service()方法,调用完毕后,按照与进入时相反的顺序,从过滤器filter n开始,依次经过各个过滤器,直到过滤器filter 1.最终将处理后的结果返回给服务器,服务器再反馈给用户。

filter进行拦截的方式也很简单,在HttpServletRequest到达Servlet之前,filter拦截客户的HttpServletRequest,根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据。在HttpServletRequest到达客户端之前,拦截HttpServletRequest,根据需要检查HttpServletRequest,也可以修改HttpServletResponse头和数据。

3.1 Filter 过滤结合 Servlet 的使用

想要让 Filter 可以过滤用户对 Servlet 发送的请求,必须满足如下两个条件:

  • 第一个:在 Filter 过滤器当中的 doFilter() 方法中编写:chain.doFilter(request, response) 方法:该方法的作用是:执行下一个过滤器,如果下面没有过滤器了(Filter 过滤器之间的 映射路径是相同的情况下),执行最终的Servlet(在Servlet 与 Filter 过滤器的映射路径是相同的情况下。)
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet
chain.doFilter(request,response);
}
  • 第二:用户发送的请求路径是否和Servlet的请求路径一致。而 Filter过滤器的映射路径是否包含/和Servlet的请求路径一致。只有 Filter 过滤器映射路径包含/和 Servlet 的请求映射路径是一致的,Filter才会过滤该用户方法的请求信息。
  • 注意:Filter的优先级,天生的就比Servlet优先级高。:比如:/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。

举例:

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter; import java.io.IOException; @WebFilter("/a.do")
public class TestFilter implements Filter { @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("TestFilter 中的 doFilter() 方法 begin 开始执行了"); // 表示:执行下一个 Filter(同一个 映射的路径,如果有下一个Filter 的话),没有就执行(同一个映射的路径的 Servlet )
chain.doFilter(request,response); System.out.println("TestFilter 中的 doFilter() 方法 end 执行结束");
} }
package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/a.do")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
System.out.println("AServlet 执行了");
}
}

测试效果:

案例举例:

LoginFilter 过滤器,获取到客户端发送过来的请求,进行一个过滤,判断用户的账号和密码是否正确,正确的话,LoginFilter 过滤器放行,到 LogServlet 表示登录成功。如果用户的账号和密码是错误的,则让提示用户密码错误,请重新登录。

这里为了方便演示核心,我们就将 账号和密码写死了,账号为: admin ,密码为 123

package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/login")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest requ, ServletResponse resp, FilterChain chain) throws IOException,
ServletException { HttpServletRequest request = (HttpServletRequest) requ;
HttpServletResponse response= (HttpServletResponse) resp; request.setCharacterEncoding("UTF-8"); // 设置获取到的请求信息的字符编码: // 获取到用户的请求信息 String name = request.getParameter("user");
String password = request.getParameter("password"); // 过滤器:判断用户登录的账号和密码是否正确
if ("admin".equals(name) && "123".equals(password)) {
// 正确:放行
// 表示:执行下一个 Filter(同一个 映射的路径,如果有下一个Filter 的话),没有就执行(同一个映射的路径的 Servlet )
chain.doFilter(request,response); } else {
// 跳转至登录失败的页面
response.sendRedirect(request.getContextPath()+"/error.jsp");
}
}
}
package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException;
import java.io.PrintWriter; @WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charSet=utf-8"); PrintWriter writer = response.getWriter(); writer.println("登录成功");
}
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body> <h3>登录失败,请重新登录</h3> </body>
</html>

Filter 过滤器的生命周期

当Web容器启动时,会根据web.xml中声明的filter顺序依次实例化这些filter。然后在Web应用程序加载时调用init()方法,随机客户端有请求时调用doFilter()方法,并且根据实际情况的不同,doFilter()方法可能被调用多次。最后Web应用程序卸载时调用destroy()方法。

Filter 过滤器与 Servlet 的区别:

  • servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
  • Filter对象默认情况下,在服务器启动的时候会新建对象。
  • Servlet是单例的。Filter也是单例的。(单实例,但是它们都是假单例,因为真单例的构造器是 private 的,而这两个类都不是私有的构造器)
  • Filter 和Servlet对象生命周期一致。
  • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。
  • Filter的优先级,天生的就比Servlet优先级高。比如:/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。

4. Filter 过滤器的拦截路径:

关于Filter的配置路径:不同的 Filter 映射路径,所拦截用户请求的也是不一样的。

关于 Filter 过滤器路径的配置:大致可以分别如下四种:

  • 精确匹配路径:/a.do、/b.do、/dept/save。这些配置方式都是精确匹配。

  • 目录匹配:/admin/*

  • 匹配所有路径: /*
  • 前后缀名路径匹配:后缀:*.do 后缀匹配。不要以 / 开始,前缀:/dept/* 前缀匹配。

4.1 精确匹配路径

只有完美匹配,一个符号,一个字符,不能错误的路径。Filter 只会过滤用户访问该:/target/dep路径的信息才会拦截判断是否放行。其他用户访问的路径一概不会进行 Filter 过滤器过滤

<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/target/dep</url-pattern> <!--精确匹配-->
</filter-mapping>

4.2 目录匹配

Filter 会过滤用户访问该:/admin/* admin子路径包含 admin路径的信息:比如:/admin/test/admin/test/test2才会拦截判断是否放行。其他用户访问的路径一概不会进行 Filter 过滤器过滤。

<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/admin/*</url-pattern> <!--目录匹配-->
</filter-mapping>

4.3 前后缀名路径匹配

后缀路径匹配:以上配置的路径,表示请求地址必须以.do 结尾才会被 Filter 过滤器拦截判断是否放行。

注意的是:不要以 / 开始 ,不然就失效了。

<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>*.do</url-pattern> <!--目录匹配-->
</filter-mapping>

前缀路径匹配:以上配置的路径,表示请求地址必须以/admin/ 开头才会被 Filter 过滤器拦截判断是否放行

<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/admin/*</url-pattern> <!--目录匹配-->
</filter-mapping>

4.4 所有路径匹配

/* 表示对应任何请求地址,Filter 过滤器都会进行一个拦截判断是否放行,那么这个路径的资源不存在也会,进行一个拦截判断是否放行。这种方式虽然简单,但是,这个代价比较到,效率低,对于特殊的路径请求要放行的你可能需要编写大量的逻辑判断进行一个拦截放行。不建议使用。

<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/*</url-pattern> <!--目录匹配-->
</filter-mapping>

5. 设置 Filter 执行顺序

一个 Servelt 是可以设置多个 Filter 过滤器的,当我们设置了多个 Filter 过滤器,其中 Filter 过滤器的执行顺序该如何设置呢?

从上面文章的内容,我们知道了 Filter 的映射路径设置有两种方式:

  1. 注解:@WebFilter()
  2. 配置 web.xml 文件的方式。这种方式 推荐使用

两种设置 Filter的方式不同,对于设置的执行顺序的方式也是不一样的。

对于 Filter 执行顺序的设置,虽然有两种方式,但是推荐使用 web.xml 配置文件的方式,因为使用 注解@WebFilter() 的方式的话,我们需要更加定义的类名的字母顺序的方式来,设置Filter的执行顺序,这样会改变一个类名的见名之意。而如果是使用 web.xml 配置文件的方式,直接更加 Filtet 编写的配置是先后顺序就可以了。

过滤器的调用顺序,遵循栈数据结构。

第一种:注解的方式:设置Filter过滤器的执行顺序:

注解的方式:

执行顺序是:比较 Filter 这个类名。就是你定义的这个类名 implements (实现) Filter 的类名

  • 比如:FilterA和FilterB,则先执行FilterA
  • 比如:Filter1和Filter2,则先执行Filter1

举例验证:

通过:配置 web.xml 文件的方式,如何设置 Filter 的执行顺序:

web.xml 中是:依靠 <filter-mapping>标签的配置位置,越靠上优先级越高,就越先执行其中的 Filter的 doFilter() 方法。

举例证实:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"> <filter>
<filter-name>FilterB</filter-name>
<filter-class>com.RainbowSea.filter.FilterB</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterB</filter-name>
<url-pattern>/A</url-pattern>
</filter-mapping> <filter>
<!-- 两个 name 名是要保持一致的-->
<filter-name>FilterA</filter-name>
<!-- 对应的全类路径名,全类限定名-->
<filter-class>com.RainbowSea.filter.FilterA</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterA</filter-name>
<!-- Filter 的映射路径,以 / 开始-->
<url-pattern>/A</url-pattern>
</filter-mapping> </web-app>
package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/A")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException,
IOException {
System.out.println("AServlet doGet() 执行了"); }
}
package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import java.io.IOException; public class FilterB implements Filter { @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException { System.out.println("FilterB doFilter() 执行了"); // 表示执行后面的(映射路径相同的Filter 过滤器),如果后面没有的话执行(映射路径相同的 Servlet)
chain.doFilter(request,response); }
}
package com.RainbowSea.filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse; import java.io.IOException; public class FilterA implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("FilterA doFilter() 执行了"); // 表示执行后面的(映射路径相同的Filter 过滤器),如果后面没有的话执行(映射路径相同的 Servlet)
chain.doFilter(request,response);
}
}

6. Filter 过滤器中的责任链设计模式思想

23种设计模式 : 责任链设计模式

过滤器最大的优点:

在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。

责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序。在上面对于 Filter的使用当中,我们已经体验到了,Filter 的动态调用其中的 doFilter() 方法,通过修改其中的 web.xml 对 Filter 的配置顺序。

下面我们演示一下不是 :责任链设计模式的方式:

如下这种方式:是我们写死了的,想要改变其中的执行顺序,就必须通过修改其中的源码当中的,代码的执行顺序,无法通过通过配置文件的方式,修改调用的顺序。

package com.RainbowSea.filter;

public class Test {
public static void main(String[] args) {
m1();
} private static void m1() {
System.out.println("m1() begin ");
m2();
System.out.println("m1() end ");
} private static void m2() {
System.out.println("m2() begin ");
m3();
System.out.println("m2() end ");
} private static void m3() {
System.out.println("m3() begin "); System.out.println("m3() end ");
}
}

7. 运用 Filter 过滤器的方式优化 oa 项目的一个登录验证:

关于 oa 项目的,大家可以移步至 B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能_ChinaRainbowSea的博客-CSDN博客 先了解,有助于后面的阅读。

思路:

有什么情况下不能拦截: 要过滤通过:

目前编写的路径是/* 表示所有请求均拦截:

用户访问 index.jsp 的时候不能拦截, 要放行:

用户登录过了,这个时候,需要放行。

用户要去登录,这个也是不能拦截,要放行:让用户登录。

其他情况:比如,没有登录过,就想访问资源的话,过滤拦截,

登录失败,过滤拦截

用户退出,不能拦截,要让其过去

需要注意的是:这里我们 Filter 过滤器当中的 doFilter() 方法中的 request 是来自: ServletRequest 接口的,当时如下使用的一些方法 sendRedirect() 是来自于: HttpServletRequest 这个接口的,所以我们需要强制类型转换一下。 HttpServletRequest 是 extends 继承了 ServletRequest 接口的。

package com.RainbowSea.Filter;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import java.io.IOException; @WebFilter("/*")
public class LoginCheckFilter implements Filter { @Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 需要强制类型转换以下,因为 下面的一些getServletPath 方法是来自 HttpServletRequest的
//不是 来自 ServletRequest 的
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res; String servletPath = request.getServletPath(); // 获取到浏览器当中的uri // 获取session 这个 session 是不不需要新建的
// 只是获取当前session ,获取不到这返回null,
HttpSession session = request.getSession(false); // 获取到服务器当中的session ,没有不会创建的 // 过滤器:
/*
有什么情况下不能拦截: 要过滤通过:
1. 目前编写的路径是/* 表示所有请求均拦截: 用户访问 index.jsp 的时候不能拦截, 要放行:
用户登录过了,这个时候,需要放行。
用户要去登录,这个也是不能拦截,要放行:让用户登录。
其他情况:比如,没有登录过,就想访问资源的话,过滤拦截,
登录失败,过滤拦截
用户退出,不能拦截,要让其过去 if ("/dept/list".equals(servletPath)) {
doList(request, response);
} else if ("/dept/detail".equals(servletPath)) {
doDetail(request, response);
} else if ("/dept/delete".equals(servletPath)) {
doElete(request,response);
} else if("/dept/save".equals(servletPath)) {
doSave(request,response);
} else if("/dept/modify".equals(servletPath)) {
doModify(request,response); */ if("/index.jsp".equals(servletPath) || (("/welcome").equals(servletPath)) ||
( session != null && session.getAttribute("username") != null)
|| "/user/login".equals(servletPath) || "/user/exit".equals(servletPath)) {
// 双重的判断,一个是 session 会话域要存在,其次是 会话域当中存储了名为 "username" 的信息 chain.doFilter(request,response); // 放行,让其向下一个过滤器,或者是Servlet 执行
} else {
response.sendRedirect(request.getContextPath() + "/index.jsp"); // 访问的web 站点的根即可,自动找到的是名为 index.jsp
// 的欢迎页面(注意这里被优化修改了:局部优先)注意:这里修改了,需要指明index.jsp登录页面了,因为局部优先
}
}
}

8. 总结:

  1. Filter 过滤器它是 JavaEE 的规范。也就是接口Filter 过滤器它的作用是:拦截请求,过滤响应。
  2. Filter 过滤器的常用的三个方法:
  • init方法:在Filter对象第一次被创建之后调用,并且只调用一次。
  • doFilter方法:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。
  • destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。
  1. 目标Servlet是否执行,取决于两个条件:
  • 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
  • 第二:用户发送的请求路径是否和Servlet的请求路径一致。
  1. chain.doFilter(request, response); 这行代码的作用:执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。

  2. 注意:Filter的优先级,天生的就比Servlet优先级高。

  • /a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。
  1. Filter 的执行原理图:

  2. Filter 过滤器拦截路径:

    • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
    • /* 匹配所有路径。
    • *.do 后缀匹配。不要以 / 开始
    • /dept/* 前缀匹配
  3. Filter 过滤器的设置执行顺序:两种方式:

  • 在web.xml文件中进行配置的时候,依靠filter-mapping标签的配置位置,越靠上优先级越高。

  • @WebFilter的时候,执行顺序是:比较Filter这个类名。

    • 比如:FilterA和FilterB,则先执行FilterA。
    • 比如:Filter1和Filter2,则先执行Filter1
  1. Filter 过滤器中的责任链设计模式思想:责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序
  2. 编写 Filter 过滤器的技巧:先想明白一个就是:哪些资源/功能方法是需要验证才能放行的,哪些是不需要验证直接就放行通过的。想明白这两点,基本上编写的 Filter 过滤器没有任何问题了。

9. 最后:

感谢如下博主的分享:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期!!!

JavaWeb 中 Filter过滤器的更多相关文章

  1. JavaWeb中监听器+过滤器+拦截器区别、配置和实际应用

    JavaWeb中监听器+过滤器+拦截器区别.配置和实际应用 1.前沿上一篇文章提到在web.xml中各个元素的执行顺序是这样的,context-param-->listener-->fil ...

  2. JavaWeb之Filter过滤器

    原本计划这一篇来总结JSP,由于JSP的内容比较多,又想着晚上跑跑步减减肥,所以今天先介绍Filter以及它的使用举例,这样的话还有些时间可以锻炼锻炼.言归正传,过滤器从字面理解她的话有拦网.过滤的功 ...

  3. 【JavaWeb】Filter 过滤器

    Filter 过滤器 简介 Filter 过滤器是 JavaWeb 三大组件之一 Filter 过滤器是 JavaEE 的规范,也就是接口 Filter 过滤器的作用是 拦截请求,过滤响应 拦截请求的 ...

  4. javaweb之Filter过滤器详解

    快速入门 1.新建一个类,实现Filter接口 2.实现doFilter()方法,打印一句话,来证明能够进行拦截 3.在web.xml中进行配置(参照Servlet配置) 4.访问一个页面,看看能不能 ...

  5. servlet中filter(过滤器)的学习使用

    servlet过滤器是小型的web组件,它能够处理传入的请求和传出的响应.Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理r ...

  6. JavaWeb中filter的详解及应用案例

    一:Filter介绍 Filter可认为是Servlet的一种“变种”,它主要用于对用户请求(HttpServletRequest)进行预处理,也可以对服务器响应(HttpServletRespons ...

  7. yii框架中关于控制器中filter过滤器和外部action的使用

    在yii框架中,控制器的过滤器分为执行前和执行后,这里举例是在执行控制器前的过滤. 需要在components/文件夹下定义公共的TestAction.php文件,并且实现run()方法.这个acti ...

  8. Web.xml中Filter过滤器标签几个说明

    在研究liferay框架中看到Web.xml中加入了过滤器的标签,可以根据页面提交的URL地址进行过滤,发现有几个新标签没用过,下面就介绍以下几个过滤器的标签用法: <!-- 定义Filter ...

  9. java中Filter过滤器处理中文乱码的方法

    注意问题:在学习用selvert的过滤器filter处理中文乱码时,在filter配置初始化时用了utf-8处理中文乱码,而在提交的jsp页面中却用了gbk.虽然两种都可以出来中文乱码,但是却造成了处 ...

  10. JavaWeb -- Servlet Filter 过滤器

    1. Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter.通过Filter技术,开发人员可以实现用户在 ...

随机推荐

  1. tuxedo How To Disable Server Side Trace Which IS Enabled BY Client?

    影响版本: Oracle Tuxedo - Version 8.1 to 11.1.1.2.0Information in this document applies to any platform. ...

  2. Strus框架

    一.框架概述 1.框架的意义与作用: 所谓框架,就是把一些繁琐的重复性代码封装起来,使程序员在编码中把更多的经历放到业务需求的分析和理解上面. 特点:封装了很多细节,程序员在使用的时候会非常简单. 2 ...

  3. Keil Jlink没法找到STM32H750

    https://www.amobbs.com/thread-5713382-1-1.html MDK使用的是5.32,jlink使用的是9.2jlink驱动使用的是6.44b 删除工程下的JLinkS ...

  4. (转)codeblocks 使用研究

    原帖:http://github.tiankonguse.com/blog/2014/10/11/codeblocks-shutcut.html 使用前 使用 codeblocks 前肯定是下载安装 ...

  5. meterpreter的使用

    meterpreter是metasploit中的一个杀手锏,通常在漏洞利用成功后,会返回给攻击者一个攻击通道,其中有很多自动化操作 场景布置 生成木马 首先,我们使用metasploit中的另一个后门 ...

  6. 【ASP.NET Core】修改Blazor.Server的Hub地址后引发的问题

    Blazor Server,即运行在服务器上的 Blazor 应用程序,它的优点是应用程序在首次运行时,客户端不需要下载运行时.但它的代码是在服务器上执行的,然后通过 SignalR 通信来更新客户端 ...

  7. Python实战项目-10文件存储/支付宝支付/支付成功回调接口

    文件存储 视频文件存储在某个位置,如果放在自己服务器上 放在项目的media文件夹 服务器上线后,用户既要访问接口,又需要看视频,都是使用一个域名和端口 分开:问价你单独放在文件服务器上,文件服务器带 ...

  8. 卡特兰路径和q,t-enumeration 学一半的笔记

    目录 卡特兰 The1st q-analogue of \(C_n\) The 2nd q-analogue of \(C_n\) /定义\(C_n(q)\) The q-Vandermonde co ...

  9. 手撕Ford-Fulkerson algorithm 学一半的笔记

    目录 定义大概就这些 伪代码 自己做slide里的quiz 搬运别人的代码 我明白了, 余量网络 名如其名 比如你f/c=3/5 那么正边2,reverse edge3,加起来是5 在这个你建的新图上 ...

  10. 磁盘IO 基本常识

    计算机硬件性能在过去十年间的发展普遍遵循摩尔定律,通用计算机的 CPU主频早已超过3GHz,内存也进入了普及DDR4的时代.然而传统硬盘虽然在存储容量上增长迅速,但是在读写性能上并无明显提升,同时SS ...