今天,我们就通过 Servlet Filter、Spring Interceptor 这两个 Java 开发中常用的组件,来具体讲讲它在框架开发中的应用。
Servlet Filter Servlet Filter 是 Java Servlet 规范中定义的组件,翻译成中文就是过滤器,它可以实现对 HTTP 请求的过滤功能,比如鉴权、限流、记录日志、验证参数等等。因为它是 Servlet 规范的一部分,所以,只要是支持 Servlet 的 Web 容器(比如,Tomcat、Jetty 等),都支持过滤器功能。为了帮助你理解,我画了一张示意图阐述它的工作原理,如下所示。
在实际项目中,我们该如何使用 Servlet Filter 呢?我写了一个简单的模拟示例代码,如下所示。首先定义如下 Filter 接口,并实现 AFilter 和 BFilter。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public interface Filter { void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) ; } public class AFilter implements Filter { @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) { System.out.println("AFilter execute Start..." ); chain.doFilter(request, response); System.out.println("AFilter execute End..." ); } } public class BFilter implements Filter { @Override public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain) { System.out.println("BFilter execute Start..." ); chain.doFilter(request, response); System.out.println("BFilter execute End..." ); } }
其中 FilterChain 就是处理器链,由于 Servlet 只是一个规范,并不包含具体的实现,所以,Servlet 中 的 FilterChain 只是一个接口定义。具体的实现类由遵从 Servlet 规范的 Web 容器来提供,比如,ApplicationFilterChain 类就是 Tomcat 提供的 FilterChain 的实现类,下面我们给出 FilterChain 和 ApplicationFilterChain 的模拟 示例代码。
为了让代码更易读懂,我对代码进行了简化,只保留了跟设计思路相关的代码片段。完整的代码你可以自行去 Tomcat 中查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public interface FilterChain { void doFilter (ServletRequest request, ServletResponse response) ; void addFilter (Filter filter) ; } public class ApplicationFilterChain implements FilterChain { private List<Filter> filterList = new ArrayList<>(); private int pos = 0 ; private int n = 0 ; @Override public void addFilter (Filter filter) { this .filterList.add(filter); n++; } @Override public void doFilter (ServletRequest request, ServletResponse response) { if (pos < n) { Filter filter = filterList.get(pos++); filter.doFilter(request, response, this ); } else { System.out.println("Start Execute Service..." ); } } }
ApplicationFilterChain 中的 doFilter() 函数的代码实现比较有技巧,实际上是一个递归调用。这样实现主要是为了在一个 doFilter() 方法中,支持双向拦截,既能拦截客户端发送来的请 求,也能拦截发送给客户端的响应。如下我们模拟执行上述过滤器示例。
1 2 3 4 5 6 7 8 9 public class Application { public static void main (String[] args) { FilterChain chain = new ApplicationFilterChain(); chain.addFilter(new AFilter()); chain.addFilter(new BFilter()); chain.addFilter(new CFilter()); chain.doFilter(new ServletRequest(), new ServletResponse()); } }
执行结果如下所示:
1 2 3 4 5 6 7 AFilter execute Start... BFilter execute Start... CFilter execute Start... Start Execute Service... CFilter execute End... BFilter execute End... AFilter execute End...
Spring Interceptor 刚刚讲了 Servlet Filter,现在我们来讲一个功能上跟它非常类似的东西,Spring Interceptor,翻译成中文就是拦截器。尽管英文单词和中文翻译都不同,但这两者基本上可以看作一个概念,都用来实现对 HTTP 请求进行拦截处理。
它们不同之处在于,Servlet Filter 是 Servlet 规范的一部分,实现依赖于 Web 容器。 Spring Interceptor 是 Spring MVC 框架的一部分,由 Spring MVC 框架来提供实现。客户端发送的请求,会先经过 Servlet Filter,然后再经过 Spring Interceptor,最后到达具体的业务代码中。我画了一张图来阐述一个请求的处理流程,具体如下所示。
在项目中,我们该如何使用 Spring Interceptor 呢?我写了一个简单的模拟示例。首先,自定义如下拦截器接口,并实现 AInterceptor 和 BInterceptor 两个拦截器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public interface HandlerInterceptor { boolean preHandle (HttpServletRequest request, HttpServletResponse response) ; void postHandle (HttpServletRequest request, HttpServletResponse response) ; void afterCompletion (HttpServletRequest request, HttpServletResponse response) ; } public class AInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response) { System.out.println("Start execute AInterceptor..." ); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response) { System.out.println("Close execute AInterceptor..." ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response) { System.out.println("Execute AInterceptor..." ); } } public class BInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response) { System.out.println("Start execute BInterceptor..." ); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response) { System.out.println("Close execute BInterceptor..." ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response) { System.out.println("Execute BInterceptor..." ); } }
它也是基于职责链模式实现的。其中,HandlerExecutionChain 类是职责链模式中的处理器链。它的实现相较于 Tomcat 中的 ApplicationFilterChain 来说,逻辑更加清晰,不需要使用递归来实现,主要是因为它将请求和响应的拦截工作,拆分到了两个函数中实现。HandlerExecutionChain 的代码如下所示,同样,我对代码也进行了一些简化和修改,但大体思路并未改动。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class HandlerExecutionChain { private List<HandlerInterceptor> interceptors = new ArrayList<>(); private int interceptorIndex = -1 ; public void addInterceptor (HandlerInterceptor interceptor) { interceptors.add(interceptor); } boolean applyPreHandle (HttpServletRequest request, HttpServletResponse response) { for (HandlerInterceptor interceptor : interceptors) { interceptorIndex++; if (!interceptor.preHandle(request, response)) { triggerAfterCompletion(request, response); return false ; } } return true ; } void applyPostHandle (HttpServletRequest request, HttpServletResponse response) { for (int i = interceptors.size() - 1 ; i >= 0 ; i--) { HandlerInterceptor interceptor = interceptors.get(i); interceptor.postHandle(request, response); } } void triggerAfterCompletion (HttpServletRequest request, HttpServletResponse response) { for (int i = interceptorIndex; i >= 0 ; i--) { HandlerInterceptor interceptor = interceptors.get(i); interceptor.afterCompletion(request, response); } } }
如下我们模拟 Spring Interceptor 的执行,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Application { public static void main (String[] args) { HandlerExecutionChain chain = new HandlerExecutionChain(); chain.addInterceptor(new AInterceptor()); chain.addInterceptor(new BInterceptor()); chain.addInterceptor(new CInterceptor()); HttpServletRequest httpServletRequest = new HttpServletRequest(); HttpServletResponse httpServletResponse = new HttpServletResponse(); boolean isNext = chain.applyPreHandle(httpServletRequest, httpServletResponse); if (!isNext) { return ; } System.out.println("start execute Controller" ); chain.applyPostHandle(httpServletRequest, httpServletResponse); chain.triggerAfterCompletion(httpServletRequest, httpServletResponse); } }
执行结果如下:
1 2 3 4 5 6 7 Start execute AInterceptor... Start execute BInterceptor... start execute Controller Close execute BInterceptor... Close execute AInterceptor... Execute BInterceptor... Execute AInterceptor...
在 Spring 框架中,DispatcherServlet 的 doDispatch() 方法来分发请求,它在真正的业务逻辑执行前后,执行 HandlerExecutionChain 中的 applyPreHandle() 和 applyPostHandle() 函数,用来实现拦截的功能。具体的代码实现很简单,你自己应该能脑补出来,这里就不罗列了。感兴趣的话,你可以自行去查看。