Java Filter过滤器的使用教程
@每天都要敲代码 人气:0一、过滤器的使用以及实现原理
当前的oa项目还存在什么缺陷
①对于DeptServlet、EmpServlet、OrderServlet,每一个Servlet都是处理自己相关的业务;在这些Servlet执行之前都是需要判断用户是否登录了。如果用户登录了,可以继续操作,如果没有登录,需要用户登录。
②这段判断用户是否登录的代码是固定的,并且在每一个Servlet类当中都需要编写,显然代码没有得到重复利用。包括每一个Servlet都要解决中文乱码问题,也有公共的代码。这些代码目前都是重复编写,并没有达到复用。
③怎么解决这个问题?
可以使用Servlet规范中的Filter过滤器来解决这个问题。
Filter作用与执行原理
①Filter是过滤器。
②Filter可以在Servlet这个目标程序执行之前添加代码,也可以在目标Servlet执行之后添加代码;之前之后都可以添加过滤规则!
③一般情况下,都是在过滤器当中编写公共代码。
过滤器怎么写
(1)编写一个Java类实现javax.servlet.Filter接口,并且实现这个接口当中所有的方法;有三个方法:
①init方法:在Filter对象第一次被创建之后调用,并且只调用一次。
②doFilter方法:只要用户发送一次请求,则执行一次;发送N次请求,则执行N次。在这个方法中编写过滤规则!
③destroy方法:在Filter对象被释放/销毁之前调用,并且只调用一次。
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:MyFilter */ public class MyFilter implements Filter { public MyFilter() { System.out.println("无参数构造方法执行"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法执行"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("Filter1 doFilter方法开始执行"); } @Override public void destroy() { System.out.println("destroy方法执行"); } }
(2)在web.xml文件中对Filter进行配置,这个配置和Servlet很像。
或者使用注解:@WebFilter({"*.do"})
<?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>myfilter</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myfilter</filter-name> <url-pattern>/a.do</url-pattern> </filter-mapping> </web-app>
(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; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:Aservlet */ @WebFilter("/a.do") public class AServlet extends HttpServlet { @Override protected 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; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:BServlet */ @WebFilter("/b.do") public class BServlet extends HttpServlet { @Override protected 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.doAservlet和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; /** * @Package:com.bjpowernode.javaweb.servlet * @Project:JavaWeb * @name:MyFilter */ public class MyFilter implements Filter { public MyFilter() { System.out.println("无参数构造方法执行"); } @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法执行"); } @Override public 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方法执行结束。"); } @Override public 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("/*")匹配所有的路径。
过滤器的调用顺序
过滤器的调用顺序,遵循栈数据结构。 下面通过一个例子来理解:
注意:在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 { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("init方法执行"); } @Override public 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方法执行结束。"); } @Override public void destroy() { System.out.println("destroy方法执行"); } }
定义一个MyFilter2过滤器
package com.bjpowernode.javaweb.servlet; import javax.servlet.*; import java.io.IOException; public class MyFilter2 implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public 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"); } @Override public void destroy() { } }
选择在xml文件中配置,对应的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>myfilter</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter1</filter-class> </filter> <filter-mapping> <filter-name>myfilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter> <filter-name>myfilter2</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.MyFilter2</filter-class> </filter> <filter-mapping> <filter-name>myfilter2</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> </web-app>
定义一个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 { @Override protected 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;所以最终结果如下:
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文件
<filter> <filter-name>loginfilter</filter-name> <filter-class>com.bjpowernode.oa.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>loginfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
②编写一个过滤器,作为判断的登录
目前写的路径是:/* 表示所有的请求均拦截,所以要先分析一下,什么情况下不能拦截?
加载全部内容