过滤器是什么?
抽象点来说, 就是介于Servlet之间的独立的元件, 可以随时加入到应用程序之中, 也可以随时移除.
具体点来说, 比如针对某些特定的页面, 只有特定的用户才能浏览, 这个时候我们就希望能把所有登录的用户进行过滤, 只给一些用户访问的权限, 这就可以在Servlet之前实现一个用户验证的过滤器.
过滤器通常搭配封装器来使用. 关于封装器可分为请求封装器和响应封装器, 具体概念及用法将在下面介绍.
1. 实现与设置过滤器
在Servlet中要实现过滤器, 必须要实现Filter接口, 并使用@WebFilter标注或在web.xml中定义过滤器, 让容器知道应该加载哪些过滤器类.
Filter接口有三个需要实现的方法:init(), doFilter(), destroy(), Filter接口的代码如下:
public interface Filter{
public void init(FilterConfig filterConfig) throws ServletException;
//FilterConfig类似与Servlet接口init方法参数上的ServletConfig(针对每个Servlet,容器都会为其生成一个ServletConfig对象),
//FilterConfig是实现Filter接口的类上使用标注或web.xml中过滤器设置信息的代表对象.
//如果在定义过滤器时设置了初始参数, 则可以通过FilterConfig的getInitparameter()方法来取得初始参数.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
public void destroy();
}
重点来看一下doFilter()方法:
doFilter()方法类似与Servlet接口的service()方法. 当请求来到容器时, 容器发现在Servlet的service()方法之前可以应用过滤器时, 就会调用过滤器的doFilter()方法. 可以在doFilter()方法中进行service()的前置处理, 而后决定是否要调第三个参数chain的doFilter()方法, 即决定是否要运行下一个过滤器.
如果调用了chain的doFilter()方法, 就会运行下一个过滤器, 如果没有过滤器了, 就会运行Servlet的service()方法. 陆续调用完过滤器的Filter()至service()后, 流程会以堆栈顺序返回.
所以在FilterChain的doFilter()运行完毕后, 就可以针对service()做后续处理, 如下:
//service()前置处理
chain.doFilter();
//service()后置处理
实现一个过滤器
例: 以下实现一个简单评测性能的过滤器, 用于记录请求到响应之间的时间差, 了解Servlet处理请求到响应所需花费的事件.
实现Filter接口代码如下:
@WebFilter(fileName = "performance", urlPatterns = {"/*"})
public class PerformanceFilter implements Filter{
private FilterConfig config;
@Override
public void init(FilterConfig config) throws ServletException{
this.config = config;
}
@Override
throws IOException, ServletException{
long begain = System.currentTimeMillis();
chain.doFilter(request, response);
config.getServletContext().log("Request process in " +
(System.currentTimeMillis() - begain) + " milliseconds");
}
//ServletContext.log("xxxx"): 将xxxx写进tomcat的日志里.
@Override
public void destroy(){}
}
还可以在@WebFilter()标注中设置dispatcherType来设置触发过滤器的时机, 代码如下:
@WebFilter(
filterName = "some",
urlPatterns = {"/some"},
dispatcherType = {
DispatcherType.REQUEST, /*不设置任何dispatcherType, 默认是REQUEST*/
DispatcherType.FORWORD, /*通过RequestDispatcher的forword()而来的请求可以套用过滤器*/
DispatcherType.INCLUDE, /*通过RequestDispatcher的include()而来的请求可以套用过滤器*/
DispatcherType.ERROR, /*由容器处理例外而转发过来的请求可以触发过滤器*/
DispatcherType.ASYNC, /*异步处理的请求可以触发过滤器*/
}
)
如果有某个url或者Servlet会应用多个过滤器, 则根据在web.xml中出现的先后顺序来决定过滤器的运行顺序.
2. 请求封装器
请求封装器是什么? 就是对request的已有方法进行扩充并封装.
实现一个封装器
例: 实现字符替换过滤器: 假设有一个留言板程序, 有些用户会在留言中输入一些HTML标签, 基于安全性考虑, 我们不希望用户输入的标签被浏览器当作网页的一部分, 所以要使用过滤器将<这样的符号替换为HTML实体字符<这样.
现在的问题是, 我们用来取得用户留言的getParameter()函数中并没有过滤HTML字符这样的设置, 我们也无法直接重定义HttpServletRequest接口的这些方法. 于是, Servlet给出了一个类: HttpServletRequestWrapper.
HttpServletRequestWrapper类实现了HttpServletRequest接口, 只要继承了HttpServletRequestWrapper类, 就可以重定义HttpServletRequest接口里面的方法, 当然也包括getParameter().
如下代码通过继承HttpServletRequestWrapper类实现了一个替换字符的请求封装器:
public class EscapeWrapper extends HttpServletRequestWrapper{
//EscapeWrapper类继承了HttpServletRequestWrapper
pubic EscapeWrapper(HttpServletRequest request){
super(request); /*必须调用父类的构造器, 确保请求传入的是此构造器*/
//定义了一个接收请求的构造器,并用super()调用父类HttpServletRequestWrapper接收请求的构造器,
//这也说明真正的请求, 将不再通过HttpServletRequestWrapper的构造器传入,
//而是通过EscapeWrapper类的构造器(即此构造器)传入
}
@Override
public String getParameter(String name){
//重定义getParameter()方法
String value = getRequest().getParameter(name);
return StringEscapeUtils.escapeHtml(value);
//将取得的请求参数值进行字符替换
//StringEscapeUtils是Apache Commons Lang程序库中的一个字符串操作类
}
}
使用上面的封装器搭配下面的过滤器来进行字符过滤的功能, 过滤器的代码如下:
@WebFilter("/*")
public class EscapeFilter implements Filter{
public void init(FilterConfig fconfig) throws ServletException{}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException{
HttpServletRequest requestWrapper = new EscapeWrapper((HttpServletRequest) request);
//将原请求对象包裹至前面写好的封装器中
chain.doFilter(requestWrapper, response);
//将封装器中的request传入doFilter()中, 执行下一个过滤器或Servlet的service()方法
}
}
3. 响应封装器
前面说完了请求封装器, 现在来说说响应封装器. 类似于请求封装器, 响应封装器就是对response的已有方法进行扩充并封装.
与HttpServletRequest对象相似, 封装器通过继承HttpServletResponseWrapper类(父类:ServletResponseWrapper类)来对HttpServletResponse对象的方法进行重定义扩充并封装.
关于响应封装器的例子(比如对响应内容的压缩处理)在这里就不再给出, 处理方法与请求封装器相似, 也是封装器搭配过滤器使用.