目录
一.过滤器作用
Filter 过滤器是 Servlet2.3 中所提供的一个过滤请求与响应的对象。
Filter 过滤器既可以对客户端向服务器端发送的请求进行过滤,也可以对服务器端向客户端产生的响应进行过滤处理。
二.Filter对象的创建
创建一个 Class 实现 Filter 接口,并实现接口中三个抽象方法。
- init()方法:初始化方法,在创建Filter后立即调用。可用于完成初始化动作。
- doFilter()方法:拦截请求与响应方法,可用于对请求和响应实现预处理。
- destroy()方法:销毁方法,在销毁Filter之前自动调用。可用于完成资源释放等动作。
容器启动时,就会创建Filter对象,Filter对象被实例化后会立即调用init函数,容器关闭时,才会销毁Filter对象。
FirstFilter.java:
package com.first.filter;
import javax.servlet.*;
import java.io.IOException;
public class FirstFilter implements Filter {
/*
当Filter对象被实例化后,会立即调用的一个方法
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Init......");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//对请求进行处理
System.out.println("请求被过滤");
//对请求做放行处理
filterChain.doFilter(servletRequest,servletResponse);
//对响应进行处理
System.out.println("响应被过滤");
}
/*
当Filter对象在销毁之前会调用一次该方法
*/
@Override
public void destroy() {
}
}
配置web.xml:
配置和servlet的配置差不多,放在<web-app>标签内
路径配置为/test/*,和上节的AnnotationServlet.java路径一样,给此servlet过滤
<filter>
<filter-name>firstFilter</filter-name>
<filter-class>com.first.filter.FirstFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>firstFilter</filter-name>
<url-pattern>/test/*</url-pattern>
</filter-mapping>
输出:
控制台输出:
请求被过滤
响应被过滤
三.在Filter中设置请求编码
需求:在 Filter 中实现对请求的编码的设置。
实际应用原因:因为当有多个servlet接受客户端请求时,而此时客户端的数据都包含了中文,如果一个个servlet去单独设置编码的话效率太低,所以可以直接在过滤器中设置编码!
控制台如果乱码的话那就是请求编码没设置。
以之前添加用户的例子(戳这里)稍加修改做演示:
getRequestDataServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
/*
获取请求数据
*/
public class getRequestDataServlet extends HttpServlet {
@Override
//客户端是以post方式传递的数据,所以这里要用doPost函数
//Tomcat拿到请求后会对请求做解析,解析出的数据会放到req对象当中
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求编码,这个必须放在获取请求数据之前,否则无效
//将请求编码注释掉,检测Filter的作用
//req.setCharacterEncoding("utf-8");
//获取用户名
String username=req.getParameter("username");
//获取用户密码
String password=req.getParameter("password");
//获取复选框中被选中的数据
String[] hobbies=req.getParameterValues("hobbies");
//转化为列表
List<String> list= Arrays.asList(hobbies);
//获取表单中的所有的key
Enumeration<String> parameterNames=req.getParameterNames();
List<String> paraList=new ArrayList<>();
while(parameterNames.hasMoreElements()){
paraList.add(parameterNames.nextElement());
}
//使用Map结构获取提交数据
Map<String,String[]> parameterMap=req.getParameterMap();
Iterator<Map.Entry<String,String[]>> iterator=parameterMap.entrySet().iterator();
resp.setContentType("text/plain;charset=utf-8");
PrintWriter pw=resp.getWriter();
pw.println("username:"+username);
//在控制台输出username,看是否乱码
//System.out.println(username);
pw.println("password:"+password);
pw.println("hobbies:"+list);
pw.println("all_key:"+paraList);
while(iterator.hasNext()){
Map.Entry<String,String[]> entry=iterator.next();
String key=entry.getKey();
String[] value=entry.getValue();
//要转化成列表再输出,直接输出value输出的是数组的地址
pw.println(key+" = "+Arrays.asList(value));
}
pw.flush();
pw.close();
}
}
addUser.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--action加个../是为了从test目录里跳出,最前面加个/是因为Context Path用了绝对路径/,
所以在最前面才可以加/,也可以不加/-->
<form action="getRequestData.do" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobbies" value="sports">体育
<input type="checkbox" name="hobbies" value="music">音乐
<input type="checkbox" name="hobbies" value="art">艺术<br>
<input type="submit" value="OK">
</form>
</body>
</html>
配置web.xml:
路径配置为/*,即匹配所有路径
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.first.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
EncodingFilter.java:
package com.first.filter;
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置请求编码
servletRequest.setCharacterEncoding("utf-8");
//对请求做放行处理
filterChain.doFilter(servletRequest,servletResponse);
}
}
输出:
控制台输出的username也没乱码,可见配置成功!
四.FilterConfig对象的使用
FilterConfig对象是用来读取<filter> 中<init-param> 初始化参数的对象。该对象通过参数传递到init方法中,用于读取初始化参数。
filterConfig.getInitParameter("name")
通过 name 获取对应的 value 。
filterConfig.getInitParameterNames()
返回该 Filter中所有<param-name> 中的值。
对上一个编码的例子进行改造
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.first.filter.EncodingFilter</filter-class>
<!-- 通过修改<param-value>达到灵活设置请求编码的功能-->
<init-param>
<param-name>code</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
EncodingFilter.java:
package com.first.filter;
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
//设置默认编码
private String defaultCode = "utf-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String code=filterConfig.getInitParameter("code");
// 如果web.xml中没有配置编码,那么就使用默认编码
if (code!=null && code.length()>0){
this.defaultCode = code;
}
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置请求编码
servletRequest.setCharacterEncoding(this.defaultCode);
//对请求做放行处理
filterChain.doFilter(servletRequest,servletResponse);
}
}
可以再运行addUser.html测试一下。
五.FilterChain(过滤器链)
Filter 技术的特点是在对请求或响应做预处理时,可实现 “ 插拔式 ” 的程序设计。我们可以根据自己需求添加多个 Filter ,也可以根据需求 去掉某个 Filter ,通过修改 web.xml 文件即可实现。那么如果有多个 过滤器对某个请求及响应进行过滤,那么这组过滤器就称为过滤器 链。
Filter 执行顺序
则按照在 web.xml 文件中配置的上下顺序来决定先后。在上的先执行,在下的后执行。
六.基于注解式开发Filter
Filter 支持注解式开发,通过 @WebFilter 注解替代 web.xml 中 Filter的配置。
使用注解式开发 Filter 时,执行顺序会根据 Filter 的 名称 进行排序的结果决定调用的顺序,即按字典序的是顺序排序。
当web.xml文件和注解都配置了Filter时,web.xml文件里面的配置优先级更高。
配置示例:
和servlet注解配置大同小异
七.Filter的生命周期
Filter 的生命周期是由容器管理的。当容器启动时会实例化 Filter 并调用 init 方法完成初始化动作。当客户端浏览器发送请求时,容器会 启动一个新的线程来处理请求,如果请求的 URL 能够被过滤器所匹 配,那么则先调用过滤器中 的 doFilter 方法,再根据是否有 chain.doFilter 的指令,决定是否继续请求目标资源。当容器关闭时 会销毁 Filter 对象,在销毁之前会调用 destroy 方法。