Filter过滤器 | 过滤器的使用以及实现原理、责任链设计模式改造oa项目
创始人
2024-04-27 17:51:55
0次

目录

一:过滤器的使用以及实现原理

(1)当前的oa项目还存在什么缺陷?

(2)Filter是什么,有什么用,执行原理是什么?

(3)过滤器怎么写?

(4)过滤器的调用顺序?

(5)Filter的生命周期?

二:责任链设计模式改造oa项目


一:过滤器的使用以及实现原理

(1)当前的oa项目还存在什么缺陷?

①对于DeptServlet、EmpServlet、OrderServlet,每一个Servlet都是处理自己相关的业务;在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了,可以继续操作,如果没有登录,需要用户登录。

②这段判断用户是否登录的代码是固定的,并且在每一个Servlet类当中都需要编写,显然代码没有得到重复利用。包括每一个Servlet都要解决中文乱码问题,也有公共的代码。这些代码目前都是重复编写,并没有达到复用。

③怎么解决这个问题?

可以使用Servlet规范中的Filter过滤器来解决这个问题。

(2)Filter是什么,有什么用,执行原理是什么?

①Filter是过滤器。

②Filter可以在Servlet这个目标程序执行之前添加代码,也可以在目标Servlet执行之后添加代码;之前之后都可以添加过滤规则!

③一般情况下,都是在过滤器当中编写公共代码。

(3)过滤器怎么写?

(1)编写一个Java类实现javax.servlet.Filter接口,并且实现这个接口当中所有的方法;有三个方法:

①init方法:在Filter对象第一次被创建之后调用,并且只调用一次。

②doFilter方法:只要用户发送一次请求,则执行一次;发送N次请求,则执行N次。在这个方法中编写过滤规则!

③destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。

package com.bjpowernode.javaweb.servlet;import javax.servlet.*;
import java.io.IOException;/*** @Author:朗朗乾坤* @Package:com.bjpowernode.javaweb.servlet* @Project:JavaWeb* @name:MyFilter* @Date:2022/12/3 15:31*/
public class MyFilter implements Filter {public MyFilter() {System.out.println("无参数构造方法执行");}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init方法执行");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("Filter1 doFilter方法开始执行");}@Overridepublic void destroy() {System.out.println("destroy方法执行");}
}

(2)在web.xml文件中对Filter进行配置,这个配置和Servlet很像。

或者使用注解:@WebFilter({"*.do"})


myfiltercom.bjpowernode.javaweb.servlet.MyFiltermyfilter/a.do

(3)打开服务器,无参构造方法和init方法都会执行;访问时doFilter方法才会执行。

①Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。

②Filter对象默认情况下,在服务器启动的时候会新建对象,执行无参构造方法。

③Servlet是单例的,Filter也是单例的。

(4)编写两个类AServlet和BServlet继承HttpServlet,并且AServlet路径定义为/a.do(和上面一样),BServlet路径定义为/b.do

package com.bjpowernode.javaweb.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @Author:朗朗乾坤* @Package:com.bjpowernode.javaweb.servlet* @Project:JavaWeb* @name:Aservlet* @Date:2022/12/3 15:40*/
@WebFilter("/a.do")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("AServlet中的doGet方法执行了。");}
}

BServlet类

package com.bjpowernode.javaweb.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @Author:朗朗乾坤* @Package:com.bjpowernode.javaweb.servlet* @Project:JavaWeb* @name:BServlet* @Date:2022/12/3 15:41*/
@WebFilter("/b.do")
public class BServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("BServlet中的doGet方法执行了。");}
}

发送请求:http://localhost:8080/servlet14/a.do 发现只有过滤器MyFilter类中的doFilter方法执行了;而AServlet中的方法并没有执行:

(5)按理说我们发送http://localhost:8080/servlet14/a.do Aservlet和MyFilter都会执行,因为路径都是/a.do ;实际上目标Servlet(Aservlet)是否执行,取决于两个条件:

①第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。

②第二:用户发送的请求路径是否和Servlet的请求路径一致。

注意:chain.doFilter(request, response); 这行代码的作用:执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet。

注意:Filter的优先级,天生的就比Servlet优先级高。/a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet!

所以不妨重写一下doFilter方法,然后再次去访问:

package com.bjpowernode.javaweb.servlet;import javax.servlet.*;
import java.io.IOException;/*** @Author:朗朗乾坤* @Package:com.bjpowernode.javaweb.servlet* @Project:JavaWeb* @name:MyFilter* @Date:2022/12/3 15:31*/
public class MyFilter implements Filter {public MyFilter() {System.out.println("无参数构造方法执行");}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init方法执行");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {// 在请求的时候添加过滤规则。System.out.println("Filter1 doFilter方法开始执行");// 执行下一个过滤器,如果下一个不是过滤器了,则执行目标程序Servlet// 简单理解就是向下走chain.doFilter(request,response);// 在响应的时候添加过滤规则。System.out.println("Filter1 doFilter方法执行结束。");}@Overridepublic void destroy() {System.out.println("destroy方法执行");}
}

执行结果如下:

三句话都执行了,所以现在对于上面那个图就更加容易理解了:

①发送请求的时候,经过了过滤器;

②执行chain.doFilter(request, response); 执行下一个过滤器或者Servlet;

③进行响应也会经过过滤器;

 补充:

①@WebFilter("/a.do") 精确匹配,只有发送a.do才会经过这个过滤器。

②@WebFilter({"/a.do", "/b.do"}) 匹配一个数组,发送a.do和b.do都可以经过这个过滤器。

③@WebFilter("*.do")  模糊匹配中的扩展匹配。以星号开始,注意这种路径不要以/开始。

④@WebFilter("/dept/*") 属于前缀匹配。要以/开始。

⑤@WebFilter("/*") 匹配所有的路径。

(4)过滤器的调用顺序?

过滤器的调用顺序,遵循栈数据结构。 下面通过一个例子来理解:

注意:在web.xml文件中进行配置的时候,Filter的执行顺序是什么?

         依靠filter-mapping标签的配置位置,越靠上优先级越高。

注意:使用注解@WebFilter的时候,Filter的执行顺序是怎样的呢?

        执行顺序是:比较Filter这个类名。

        比如:FilterA和FilterB,则先执行FilterA

        比如:Filter1和Filter2,则先执行Filter1

定义一个MyFilter1过滤器

package com.bjpowernode.javaweb.servlet;import javax.servlet.*;
import java.io.IOException;public class MyFilter1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("init方法执行");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {       System.out.println("Filter1 doFilter方法开始执行");chain.doFilter(request,response);System.out.println("Filter1 doFilter方法执行结束。");}@Overridepublic void destroy() {System.out.println("destroy方法执行");}
}

定义一个MyFilter2过滤器

package com.bjpowernode.javaweb.servlet;import javax.servlet.*;
import java.io.IOException;public class MyFilter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("Filter2 doFilter begin");chain.doFilter(request, response);System.out.println("Filter2 doFilter end");}@Overridepublic void destroy() {}
}

选择在xml文件中配置,对应的web.xml文件


myfiltercom.bjpowernode.javaweb.servlet.MyFilter1myfilter*.domyfilter2com.bjpowernode.javaweb.servlet.MyFilter2myfilter2*.do

定义一个AServlet类,路径通过注解的方式

package com.bjpowernode.javaweb.servlet;import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebFilter("/a.do")
public class AServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("AServlet中的doGet方法执行了。");}
}

Filter的优先级是比Servlet高的,所以当进行访问时:http://localhost:8080/servlet14/a.do 会先按照web.xml文件中的filter-mapping标签位置进行访问,遇到chain.doFilter(request, response)就会跳转到下一个Filter或者Servlet;所以最终结果如下:

(5)Filter的生命周期?

①Filter的生命周期和Servlet对象生命周期一致。

②唯一的区别:Filter默认情况下,在服务器启动阶段就实例化;而Servlet不会。

二:责任链设计模式改造oa项目

(1)我们先看一段java代码;这段代码也能实现过滤器的效果,遵循栈数据结构。但是这个程序的问题: 在编译阶段已经完全确定了调用关系。  如果你想改变他们的调用顺序,必须修改以下java源代码。 java代码修改,需要重新编译,项目需要重新测试,项目需要重新发布。这是一个繁琐的过程。显然,这种设计违背了:OCP原则。(开闭原则)

package com.bjpowernode.javaweb.servlet;public class Test {public static void main(String[] args) {System.out.println("main begin");m1();System.out.println("main over");}private static void m1() {System.out.println("m1 begin");m2();System.out.println("m1 over");}private static void m2() {System.out.println("m2 begin");m3();System.out.println("m2 over");}private static void m3() {System.out.println("目标正在执行中。。。。");}
}

(2)Filter过滤器里有一个设计模式 :责任链设计模式。

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

注:责任链设计模式最大的核心思想:在程序运行阶段,动态的组合程序的调用顺序!

tip:对于过滤器Filter的配置一般是配置到web.xml文件当中,不要使用注解的方式;这样更加的容易修改它们的执行顺序。

(3)使用过滤器改造OA项目

①首先配置web.xml文件

    loginfiltercom.bjpowernode.oa.filter.LoginFilterloginfilter/*

②编写一个过滤器,作为判断的登录

目前写的路径是:/* 表示所有的请求均拦截,所以要先分析一下,什么情况下不能拦截?

😊用户访问 index.jsp的时候不能拦截

😊用户已经登录了,这个需要放行,不能拦截。

😊用户要去登录,这个也不能拦截。

😊WelcomeServlet也不能拦截。

package com.bjpowernode.oa.filter;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;/*** @Author:朗朗乾坤* @Package:com.bjpowernode.oa.filter* @Project:JavaWeb* @name:LoginFilter* @Date:2022/12/3 17:39*/
public class LoginFilter implements Filter {// 也可以只重写其中一个方法@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throws IOException, ServletException {// 注意这里要先进行强转为HttpServlet,后面才能调用对应的方法HttpServletRequest request = (HttpServletRequest)req;HttpServletResponse response = (HttpServletResponse) resp;// 获取请求路径String servletPath = request.getServletPath();// 获取当前session对象,获取不到就返回nullHttpSession session = request.getSession(false);if("/index.jsp".equals(servletPath) || "/welcome".equals(servletPath) ||"/dept/login".equals(servletPath) || "/dept/exit".equals(servletPath)|| (session != null && session.getAttribute("username") != null)){// 经过上面过滤,只有上面的路径才会直接通过;其它的操作都要先跳转到登录页面// 继续往下走chain.doFilter(request, response);}else{response.sendRedirect(request.getContextPath() + "/index.jsp");}}
}

这个过滤器写好以后,以后其它类也可以调用这个过滤器,都不用验证登录了;例如:一个员工的Servlet(EmpServlet)

package com.bjpowernode.oa.web.action;import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;import java.io.IOException;/*** 员工管理的。* 员工管理的前提也是需要先登录。*/
public class EmpServlet extends HttpServlet {@Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 乱码问题也可以写入过滤器当中// post请求乱码问题request.setCharacterEncoding("UTF-8");// 响应中文乱码问题response.setContentType("text/html;charset=UTF-8");HttpSession session = request.getSession(false);if(session != null && session.getAttribute("username") != null){String servletPath = request.getServletPath();//...}else{response.sendRedirect(request.getContextPath() + "/index.jsp");}}
}

对于原来的DeptServlet类也可以进行修改了:

        HttpSession session = request.getSession(false);if (session != null && session.getAttribute("username") != null){ // session对象不一定为null// 获取servlet pathString servletPath = request.getServletPath();if ("/dept/list".equals(servletPath)){doList(request,response);}else if("/dept/detail".equals(servletPath)){doDetail(request,response);}else if("/dept/delete".equals(servletPath)) {doDel(request, response);}else if("/dept/add".equals(servletPath)) {doAdd(request, response);}else if("/dept/modify".equals(servletPath)) {doModify(request, response);}}else{// 跳转到登录页面response.sendRedirect(request.getContextPath()+"/index.jsp");}

修改为:

        String servletPath = request.getServletPath();if ("/dept/list".equals(servletPath)){doList(request,response);}else if("/dept/detail".equals(servletPath)){doDetail(request,response);}else if("/dept/delete".equals(servletPath)) {doDel(request, response);}else if("/dept/add".equals(servletPath)) {doAdd(request, response);}else if("/dept/modify".equals(servletPath)) {doModify(request, response);}

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...