1、Filter基础:
- Filter是Servlet规范的三大组件之一。顾名思义,就是过滤。可以在请求到达目标资源之前先对请求进行拦截过滤,即对请求进行一些处理;也可以在响应到达客户端之前先对响应进行拦截过滤,即对响应进行一些处理。
2、Filter的生命周期:
a、Filter的生命周期与Servlet的生命周期类似,其主要生命周期阶段有四个:Filter对象的创建、Filter对象的初始化、Filter执行doFilter()方法以及最终Filter对象被销毁。
- Filter的整个生命周期过程的执行,均由Web服务器负责管理。即Filter从创建到销毁的整个过程中方法的调用,都是由Web服务器负责调用执行的,程序员无法控制其执行流程。
- 过滤器生命周期示例:
- 自定义的Filter类需要实现Filter接口(javax.servlet.Filter),并实现其接口中定义的方法:
package com.geeklicreed.filters;
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 SomeFilter implements Filter {
public SomeFilter() {
System.out.println("SomeFilter被创建");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("SomeFilter被初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 请求拦截过滤时可以编写的代码部分
System.out.println("执行SomeFilter -- before --");
chain.doFilter(request, response);
// 响应拦截过滤时可以编写的代码部分
System.out.println("执行SomeFilter -- after --");
}
@Override
public void destroy() {
System.out.println("SomeFilter被销毁");
}
}
- 自定义Servlet类:
package com.geeklicreed.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SomeServlet extends HttpServlet {
private static final long serialVersionUID = 2884109743938532642L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("执行SomeServlet");
}
}
- 需要注意的是,需要在web.xml中注册filter:
<?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_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 注册Filter --> <filter> <filter-name>some-Filter</filter-name> <filter-class>com.geeklicreed.filters.SomeFilter</filter-class> </filter> <filter-mapping> <filter-name>some-Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>some-servlet</servlet-name> <servlet-class>com.geeklicreed.servlet.SomeServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>some-servlet</servlet-name> <url-pattern>/some</url-pattern> </servlet-mapping> </web-app>
- 启动服务器,访问项目中的/some路径资源,在控制台中显示的结果为其生命周期:
- 上例中的接口以及方法的解释:
- javax.servlet.Filter接口:过滤器是用于过滤请求资源(servlet、静态资源)或者是从资源响应客户端的过滤任务:(或者是两者兼而有之)
- 其接口中三个定义方法:
- javax.servlet.FilterChain接口:FilterChain接口类对象是被用于servlet容器提供给开发者从视图到资源过滤请求的调用链的对象。过滤器使用过滤器链来调用链中的下一个过滤器,如果正在调用的过滤器是链中的最后一个,则会直接调用链末尾端的资源。
- 该接口中只定义一个方法:调用过滤器链中的下一个过滤器,或者如果正在调用的过滤器是链中的最后一个,则会直接调用链末尾端的资源。
b、Filter的特征:
- Filter是单例多线程的。
- Filter是在应用被加载时创建并初始化的,这是与Servlet不同的地方。Servlet是在该Servlet被第一次访问时创建的。Filter与Servlet的共同点是,其无参构造器与init()方法只会执行一次。
- 用户每提交一次该Filter可以过滤的请求,服务器就会执行一次doFilter()方法,即doFilter()方法可以被多次执行。
- 当应用被停止时执行destroy()方法,Filter被销毁,即destroy()方法只会执行一次。
- 由于Filter是单例多线程的,所以为了保证其线程安全性,一般情况下是不为Filter类定义可修改的成员变量。因为每个线程均可修改这个成员变量,会出现线程安全问题。
3、javax.servlet.FilterConfig接口:通常指代filter在web.xml中的注册信息:
- FilterConfig接口中的方法可以获取关于当前Filter在web.xml中的信息:(ServletContext、filterName、initParameter等):
- FilterConfig接口中的方法在filter类中使用的示例:
- 可以在自定义Filter类中定义私有化的成员变量(并提供getFilterConfig()方法),在init(FilterConfig filterConfig)方法接收。在doFilter方法中使用这个filterConfig成员变量:
package com.geeklicreed.filters;
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 SomeFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public FilterConfig getFilterConfig() {
return filterConfig;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println(filterConfig.getFilterName());
System.out.println(filterConfig.getServletContext());
System.out.println(filterConfig.getInitParameter("username"));
Enumeration<String> initParameterNames = filterConfig
.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String initParameterName = initParameterNames.nextElement();
System.out.println(initParameterName + " --> "
+ filterConfig.getInitParameter(initParameterName));
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
- 需要注意的是,在web.xml中的<filter>标签中,可以提供<init-param>标签,以提供初始化参数:
<?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_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 注册Filter --> <filter> <filter-name>Some-Filter</filter-name> <filter-class>com.geeklicreed.filters.SomeFilter</filter-class> <init-param> <param-name>username</param-name> <param-value>geeklicreed</param-value> </init-param> <init-param> <param-name>age</param-name> <param-value>21</param-value> </init-param> </filter> <filter-mapping> <filter-name>Some-Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
- 当访问一个index.jsp页面时,控制台中的打印如下:
4、<dispatcher>标签的四种取值:
- 在<filter-mapping>中还有一个子标签<dispatcher>,用于设置过滤器的请求类型。其有四种取值,REQUEST、FORWARD、INCLUDE、ERROR。
- FORWARD:若请求是由一个Servlet通过RequestDsipatcher的forward()方法所转发的,那么这个请求将被<dispatcher>值为FORWARD的Filter所拦截。即当前Filter只会拦截由RequestDispatcher的forward()方法所转发的请求。其他请求均
不拦截。
- INCLUDE:当前Fillter只会拦截由RequestDsipatcher的include()方法所转发的请求。其他请求均不拦截。
- REQUEST:表示当前过滤其只会拦截普通请求,但是对forward和include的跳转不进行拦截。(默认值)
- ERROR:表示当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截。
- <dispatcher>标签的ERROR取值示例:
- 在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_3_0.xsd" id="WebApp_ID" version="3.0"> <!-- 注册Filter --> <filter> <filter-name>Some-Filter</filter-name> <filter-class>com.geeklicreed.filters.SomeFilter</filter-class> </filter> <filter-mapping> <filter-name>Some-Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping> <!-- 错误页面 --> <error-page> <error-code>404</error-code> <location>/error.jsp</location> </error-page> </web-app>
5、多个Filter的执行顺序问题:
- 当应用中存在多个Filter时,其执行顺序与注册顺序一致。(如上图所示,先执行OneFilter的请求过滤拦截,再是TwoFilter的请求过滤拦截,后是SomeServlet的执行,后是TwoFilter的响应过滤拦截,最后是TwoFilter的请求过滤拦截)
6、Filter的执行原理:
- 当某资源的请求到达Web容器时,会先对请求进行解析,使用解析出来的URI作为比较对象,从Map(Map的key为<url-pattern>的值,value为Filter实例对象的引用)中查找是否存在相匹配的key。若存在,那么读取其value,即Filter对象的引用,将该引用存入到数组中。然后继续向后查找,直到将该Map查找完毕。这样在数组中就存在按照查找顺序排好序的Filter引用。
- 数组初始化完毕后,开始按照数组元素顺序进行执行。所以数组中的Filter全部执行完毕之后,再跳转到请求的目标资源。(数组中存放着与请求相匹配的所有Filter)
7、附加说明:
a、若Filter为全路径匹配方式,那么url-pattern只能写为/*,而不能够写为/。(写为/,无论是静态资源,还是动态资源都不起作用)
- <filter-mapping>标签中可以不使用<url-pattern>,但需要指定<servlet-name>,即当前过滤器拦截的是对指定Servlet的请求。
b、filter过滤器的应用:
- 可以在自定义的过滤器(MyCharactorEncodingFilter类)中解决POST提交中文乱码问题。
package com.geeklicreed.filters;
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 MyCharactorEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//解决请求参数中文乱码问题
request.setCharacterEncoding("UTF-8");
//解决响应中文乱码问题
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
转载于:https://blog.51cto.com/12402717/1977105