创建型:单例模式,工厂模式,建造者模式,原型模式
结构型:桥接模式,代理模式,装饰器模式,适配器模式,门面模式,组合模式,享元模式
行为型:观察者模式,模板模式,策略模式,责任链模式,状态模式,迭代器模式,访问者模式
在工作中,我们经常要和Servlet Filter,Spring MVC Interceptor打交道,虽然我配置写的很6,但是出了问题还得到处google,于是看了一下源码,用Demo的方式来分析一下这两者是怎么工作的。
可能很多小伙伴没怎么用过Filter,我就简单演示一下
<filter-mapping> <filter-name>logFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>imageFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
@WebFilter(filterName = "logFilter") public class LogFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("LogFilter execute"); chain.doFilter(request, response); } }
@WebFilter(filterName = "imageFilter") public class ImageFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("ImageFilter execute"); chain.doFilter(request, response); } }
如果你在一个Filter方法后不加 chain.doFilter(request, response)
则后续的Filter和Servlet都不会执行,这是为什么呢?看完我手写的Demo你一下就明白了
可以看到Filter可以在请求到达Servlet之前做处理,如
有兴趣的小伙伴可以看看相关的源码
Servlet接口,任何一个web请求都会调用service方法
public interface Servlet { public void service(); }
public class MyServlet implements Servlet { @Override public void service() { System.out.println("MyServlet的service方法执行了"); } }
拦截器接口
public interface Filter { public void doFilter(FilterChain chain); }
public class LogFilter implements Filter { @Override public void doFilter(FilterChain chain) { System.out.println("LogFilter执行了"); chain.doFilter(); } }
public class ImageFilter implements Filter { @Override public void doFilter(FilterChain chain) { System.out.println("ImageFilter执行了"); chain.doFilter(); } }
拦截器链对象
public interface FilterChain { public void doFilter(); }
public class ApplicationFilterChain implements FilterChain { private Filter[] filters = new Filter[10]; private Servlet servlet = null; // 总共的Filter数目 private int n; // 当前执行完的filter数目 private int pos; @Override public void doFilter() { if (pos < n) { Filter curFilter = filters[pos++]; curFilter.doFilter(this); return; } servlet.service(); } public void addFilter(Filter filter) { // 这里源码有动态扩容的过程,和ArrayList差不多 // 我就不演示了,直接赋数组大小为10了 filters[n++] = filter; } public void setServlet(Servlet servlet) { this.servlet = servlet; } }
测试例子
public class Main { public static void main(String[] args) { // 在tomcat源码中,会将一个请求封装为一个ApplicationFilterChain对象 // 然后执行ApplicationFilterChain的doFilter方法 ApplicationFilterChain applicationFilterChain = new ApplicationFilterChain(); applicationFilterChain.addFilter(new LogFilter()); applicationFilterChain.addFilter(new ImageFilter()); applicationFilterChain.setServlet(new MyServlet()); // LogFilter执行了 // ImageFilter执行了 // MyServlet的service方法执行了 applicationFilterChain.doFilter(); } }
如果任意一个Filter方法的最后不加上chain.doFilter(),则后面的拦截器及Servlet都不会执行了。相信你看完ApplicationFilterChain类的doFilter方法一下就明白了,就是一个简单的递归调用
以前写过一篇拦截器应用的文章,有想了解使用方式的小伙伴可以看一下
用Spring MVC拦截器做好web应用的安保措施
今天就来分析一下拦截器是怎么实现的?可以通过以下方式实现拦截器
总而言之拦截器必须必须实现了HandlerInterceptor接口
HandlerInterceptor有如下3个方法
boolean preHandler():在controller执行之前调用
void postHandler():controller执行之后,且页面渲染之前调用
void afterCompletion():页面渲染之后调用,一般用于资源清理操作
这个图应该很好的显示了一个请求可以被拦截的地方
我来手写一个Demo,你一下就能明白了
拦截接口,为了方便我这里就只定义了一个方法
public interface HandlerInterceptor { boolean preHandle(); }
定义如下2个拦截器
public class CostInterceptor implements HandlerInterceptor { @Override public boolean preHandle() { // 这里可以针对传入的参数做一系列事情,我这里就简单返回true了; System.out.println("CostInterceptor 执行了"); return true; } }
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle() { System.out.println("LoginInterceptor 执行了"); return true; } }
存放拦截器的容器
public class HandlerExecutionChain { private List<HandlerInterceptor> interceptorList = new ArrayList<>(); public void addInterceptor(HandlerInterceptor interceptor) { interceptorList.add(interceptor); } public boolean applyPreHandle() { for (int i = 0; i < interceptorList.size(); i++) { HandlerInterceptor interceptor = interceptorList.get(i); if (!interceptor.preHandle()) { return false; } } return true; } }
演示DispatcherServlet的调用过程
public class Main { public static void main(String[] args) { // Spring MVC会根据请求返回一个HandlerExecutionChain对象 // 然后执行HandlerExecutionChain的applyPreHandle方法,controller中的方法 HandlerExecutionChain chain = new HandlerExecutionChain(); chain.addInterceptor(new CostInterceptor()); chain.addInterceptor(new LoginInterceptor()); // 只有拦截器都返回true,才会调用controller的方法 // CostInterceptor 执行了 // LoginInterceptor 执行了 if (!chain.applyPreHandle()) { return; } result(); } public static void result() { System.out.println("这是controller的方法"); } }
如果任意一个Interceptor返回false,则后续的Interceptor和Controller中的方法都不会执行原因在Demo中显而易见
当想对请求增加新的过滤逻辑时,只需要定义一个拦截器即可,完全符合开闭原则。
不知道你意识到没有 Servlet Filter和Spring MVC Interceptor都是用责任链模式实现的
来看看DispatcherServlet是怎么做的?和我们上面写的demo一模一样
我们用servlet写web应用时,一个请求地址写一个Servlet类。
而用了spring mvc后,整个应用程序只有一个Servlet即DispatcherServlet,所有的请求都发送到DispatcherServlet,然后通过方法调用的方式执行controller的方法
DispatcherServlet的doDispatch方法源码如下,省略了一部分逻辑(所有的请求都会执行这个方法)
protected void doDispatch() { // 执行所有HandlerInterceptor的preHandle方法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 执行controller中的方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 执行所有HandlerInterceptor的postHandle方法 mappedHandler.applyPostHandle(processedRequest, response, mv); }
Interceptor可以有如下用处
可以看到Servlet Filter和Spring MVC Interceptor都能对请求进行拦截,只不过时机不同。并且Servlet Filter是Servlet的规范,而Spring MVC Interceptor只能在Spring MVC中使用
[0] https://mp.weixin.qq.com/s/8AIRvz5HOgjw12PbsjZhCQ
[1] https://www.cnblogs.com/xrq730/p/10633761.html
filter源码分析
[2] https://cloud.tencent.com/developer/article/1129724
[3] https://www.jianshu.com/p/be47c9d89175