15. JSP 过滤器
过滤器filter简介
Filter 是 Servlet 规范的三大组件之一,另外两个分别是servlet和listener。filter中文意思是过滤,可以在请求到达目标资源之前先对请求进行拦截过滤,即对请求进行一些处理; 也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。
WEB开发人员通过Filter技术,可以对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息、计算系统的响应时间等一些高级功能。
在 Servlet 规范中,有一个 javax.servlet.Filter 接口。实现了该接口的类称为过滤器,接口中有三个方法可以重写:
- init():初始化方法,即 Filter 被创建后,在后面用到的资源的初始化工作,可以在这里完成。
- doFilter():Filter 的核心方法,对于请求与响应的过滤,就是在这个方法中完成的。
- destroy():销毁方法。 Filter 被销毁前所调用执行的方法。对于资源的释放工作,可以在这里完成。
这里主要是 doFilter 方法 ,他就是中间那个是过滤器,有两种可能 一种发给目标资源 一种返回去。
创建Filter过滤器的步骤
1.创建一个类实现javax.servlet.Filter接口
2.重写接口中的方法
3.在web.xml文件中配置Filter
例:
1.Filter.java
package filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class filter_test implements Filter { public filter_test() { } public void destroy() { System.out.println("过滤器已销毁×"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("过滤请求"); chain.doFilter(request, response); //通过过滤请求后 继续访问资源【如果没这句话 会直接返回给用户 用户就什么也不能干了 等于请求被拦截了】 System.out.println("过滤响应"); } public void init(FilterConfig fConfig) throws ServletException { System.out.println("过滤器已创建√"); } }
2.配置XML 【和Servlet配置差不多】
<!-- 过滤器 和Servlet 配置的XML差不多的 --> <filter> <filter-name>filter_test</filter-name> <filter-class>filter.filter_test</filter-class> </filter> <filter-mapping> <filter-name>filter_test</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
里面那个 /* 表示过滤任何的网址- 【可以过滤指定网址 * 为通配符】
注意:
1. 在Filter的doFilter方法内如果没有执行doFilter(request, response)方法,那么服务器中的资源是不会被访问到的。
2.Filter的全路径匹配只支持/*,不支持/
可以得出Filter的生命周期如下:
- 当服务器启动,会创建Filter对象,并调用init方法,只调用一次.
- 当访问资源时,路径与Filter的拦截路径匹配,会执行Filter中的doFilter方法,这个方法是真正拦截操作的方法.
- 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作.
一个Filter的生命周期跟servlet有些类似,需要经历实例化—>初始化—>doFilter—>销毁四个过程。
dispatcher 标签
在 filter-mapping 中还有一个子标签 dispatcher ,用于设置过滤器所过滤的请求类型。
其有五种取值:REQUEST、FORWARD、INCLUDE、ASYNC、ERROR,默认是REQUEST
- FORWARD【forward】
若请求是由一个 Servlet 通过 RequestDispatcher 的 forward()方法所转发的, 那么这个请求将被值为 FORWARD 的 Filter 拦截。即当前 Filter 只会拦截由RequestDispatcher 的 forward()方法所转发的请求。其它请求均不拦截。 - INCLUDE【include】
只要是通过<jsp:include page=”xxx.jsp” />,嵌入进来的页面,每嵌入的一个页面,都会走一次指定的过滤器。 - ERROR【error】
在 web.xml 中可以配置错误页面 error-page ,当发生指定状态码的错误后,会跳转到指定的页面。而这个跳转同样是发出的请求。若的值设置为 EEROR,则当前过滤器只会拦截转向错误页面的请求,其它请求不会拦截。例: -
<error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page>
- ASYNC【async】
会拦截AsyncContext对象发出的请求
- REQUEST 【request】
默认值。即不设置 dispatcher 标签,也相当于指定了其值为 REQUEST。只要请求不是由 RequestDispatcher 的 forward()方法或 include()方法转发的,那么该 Filter均会被拦截,即使是向错误页面的跳转请求,同样会被拦截
Filter特征
- Filter 是单例多线程的。
- Filter 是在应用被加载时创建并初始化, 这是与 Servlet 不同的地方。 Servlet 是在该 Servlet被第一次访问时创建。 Filter 与 Servlet 的共同点是,其无参构造器与 init()方法只会执行一次。
- 用户每提交一次该 Filter 可以过滤的请求,服务器就会执行一次 doFilter()方法,即doFilter()方法是可以被多次执行的。
- 当应用被停止时执行 destroy()方法,Filter 被销毁,即 destroy()方法只会执行一次。
- 由于 Filter 是单例多线程的,所以为了保证其线程安全性,一般情况下是不为 Filter 类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题。
FilterConfig
在Filter中的init方法上有一个参数叫FilterConfig,这是Filter的配置对象,通过FilterConfig对象可以获取当前 Filter 在 web.xml中的配置信息,这与ServletConfig类似。一个 Filter 对象一个 FilterConfig 对象,多个 Filter 对象会有多个 FilterConfig 对象。它的作用:
- 获取Filter的名称
- 获取初始化参数
-
获取ServletContext对象
FilterConfig 接口中的方法与 ServletConfig 接口中的方法,方法名与意义完全相同。【所以很好记住】
所以 这玩意 和 Servlet 很相似:Servlet有初始化参数 他也有 、而且 一个Servlet 对应一个 Config 他也是,啥都差不多....
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>filter</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>filter_test</filter-name> <filter-class>filter.filter_test</filter-class> <!-- 重点: 初始化对象要写在对应的 过滤器中! 除了ServletContext, Servlet的和 filter的都要写在指定的标签中 --> <init-param> <param-name>姓名</param-name> <param-value>小涵</param-value> </init-param> <init-param> <param-name>年龄</param-name> <param-value>20</param-value> </init-param> <init-param> <param-name>身高</param-name> <param-value>168</param-value> </init-param> <init-param> <param-name>爱好</param-name> <param-value>干饭</param-value> </init-param> <init-param> <param-name>工作</param-name> <param-value>打工人</param-value> </init-param> </filter> <filter-mapping> <filter-name>filter_test</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
filter_test.java :【过滤器】
package filter; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class filter_test implements Filter { FilterConfig fc = null; public filter_test() { } public void destroy() { System.out.println("过滤器已销毁×"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("监听器的名称:" + this.fc.getFilterName()); System.out.println("监听器的初始化对象:"); //遍历 Enumeration<String> parmenters = this.fc.getInitParameterNames(); while(parmenters.hasMoreElements()) { String name = parmenters.nextElement(); System.out.println("监听器初始化名:" + name + " --- 监听器初始化值:" + fc.getInitParameter(name)); } } public void init(FilterConfig fConfig) throws ServletException { System.out.println("过滤器已创建√"); this.fc = fConfig; if(fc != null) { System.out.println("获取fConfig成功!"); } } }
多个Filter的执行过程
若web应用中配置了多个 Filter,那么这些 Filter 的执行过程是以“链”的方式执行的。即会将这
些与请求相匹配的 Filter 串成一个可执行的“链”,然后按照这个链中的顺序依次执行。这些 Filter 在链中的顺序与它们在 web.xml 中的注册顺序相同,即 web.xml 中的注册顺序就是 Filter 的执行顺序。
一个 Filter 的执行完毕,转而执行另一个 Filter,这个转向工作是由 FilterChain 的 doFilter()方法完成的。当然,若当前 Filter 是最后一个 Filter,则 FilterChain 的 doFilter()会自动转向最终的请求资源。
当请求到达 Filter 后,Filter 可以拦截到请求对象,并对请求进行修改。修改过后,再将
该修改过的请求转向下一个资源。
当最终的资源执行完毕,并形成响应对象后,会按照请求访问 Filter 的倒序,再次访问Filter。此时 Filter 可以拦截到响应对象,并对响应进行修改。最终,客户端可以收到已被修改过的响应。