过滤器链设计模式
Java Web应用开发中过滤器Filter都用过,今天老吕带大家来看看最常用的tomcat中是如何实现 Servlet规范中过滤器链功能的。
源码讲解
javax.servlet包中关于过滤器的接口
tomcat的实现过滤器相关的类
主要看这个ApplicationFilterChain类,它实现了FilterChain接口,是关键所在。
为了便于理解代码,我把tomcat源码中和设计模式无关的代码都清理了,只保留下最关键的代码,并加了注释,个别依赖的类也进行了简单处理。
public class ApplicationFilterChain implements FilterChain {
/**
* Filters.
* 过滤器数组集合,初始数组大小为0,这就意味着后面定有扩容操作
*/
private Filter[] filters = new Filter[0];
/**
* The int which is used to maintain the current position
* in the filter chain.
* 将要执行的过滤器指针(数组下标)
*/
private int pos = 0;
/**
* The int which gives the current number of filters in the chain.
* 过滤器链上过滤器的总数量
*/
private int n = 0;
// --------------------------------------------------------------
//过滤器数组扩容容量增量值
public static final int INCREMENT = 10;
/**
* The servlet instance to be executed by this chain.
* 目标servlet,过滤器链执行完毕后直接调
*/
private Servlet servlet = null;
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
internalDoFilter(request, response);
}
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
//继续执行下一个过滤器,注意pos的++ 操作,这个是过滤器链指针指向了下一个过滤器,为下一个过滤器的执行做好准备
Filter filter = filters[pos++];
try {
//最后一个参数是关键
//这里面并没有使用for循环把所有的过滤器调用一遍,
//而是用了一个递归操作,通过传递当前FilterChain实例,将调用下下一个过滤器的决定权交给了下一个过滤器
filter.doFilter(request, response, this);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new ServletException("filterChain.filter", e);
}
//如果下一个过滤器忘记了向下传递,就会走到这里,意味着请求的中断
//再也不会调到目标servlet了 😓
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
//出了 过滤器链,向下调用servlet实例方法
servlet.service(request, response);
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
throw new ServletException("filterChain.servlet", e);
} finally {
}
}
/**
* Add a filter to the set of filters that will be executed in this chain.
* 向过滤器链上注册过滤器
* @param filterAdd The FilterConfig for the servlet to be executed
*/
void addFilter(Filter filterAdd) {
// Prevent the same filter being added multiple times
//防止添加重复的过滤器
for (Filter filter : filters) {
if (filter == filterAdd) {
return;
}
}
//过滤器数组已满,进行扩容操作,默认新增10个容量
if (n == filters.length) {
Filter[] newFilters =
new Filter[n + INCREMENT];
System.arraycopy(filters, 0, newFilters, 0, n);
filters = newFilters;
}
//将新增的过滤器追加到过滤器链上,注意n++ ,它代表了当前实际存储的过滤器个数,因为数组不一定能填满,所以n来记录是有必要的
filters[n++] = filterAdd;
}
/**
* Set the servlet that will be executed at the end of this chain.
* 设置目标servlet
* @param servlet The Wrapper for the servlet to be executed
*/
public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
}
接下来我就以上面的ApplicationFilterChain为基础来写个demo测试下这个过滤器链
Filter1、Filter2、Filter3、FilterResponseTime 为4个过滤器,其中最后一个实现了请求耗时统计功能
四个过滤器
public class Filter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器1");
//能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {}
}
public class Filter2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器2");
//能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {}
}
public class Filter3 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器3");
//能调用下一个过滤器的关键,如果没有这一句,将中断(跳出)过滤器链
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {}
}
//实现一个接口耗时统计功能
public class FilterResponseTime implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = System.currentTimeMillis();
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
long timeCost = System.currentTimeMillis() - start;
System.out.println("耗时" + timeCost + "ms");
}
}
@Override
public void destroy() {
}
}
模拟一个目标Servlet
/**
* @Title MyServlet
* @Description
* @Author lvaolin
* @Date 2021/12/26 22:36
**/
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(" -----到达 servlet service--------");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" -----结束 servlet service--------");
}
}
模拟调用上下文
/**
* @Title TomcatFilterChainTest
* @Description
* @Author lvaolin
* @Date 2021/12/26 12:54
**/
public class Main {
public static void main(String[] args) throws IOException, ServletException {
//准备过滤器链
ApplicationFilterChain filterChain = new ApplicationFilterChain();
//设置目标servlet
filterChain.setServlet(new MyServlet());
//设置过滤器集合
filterChain.addFilter(new Filter1());
filterChain.addFilter(new Filter2());
filterChain.addFilter(new Filter3());
filterChain.addFilter(new FilterResponseTime());
//过滤请求
System.out.println("-----request 开始----");
filterChain.doFilter(null, null);
}
}
运行结果
在过滤器2中中断传递后的效果
//filterChain.doFilter(servletRequest, servletResponse);
发现请求中断了,不能达到servlet了,
原因就在老吕的ApplicationFilterChain代码注释中,去看看吧。