首页 科技正文

枣庄旅游:惊呆了,Servlet Filter和Spring MVC Interceptor的实现居然这么简朴

admin 科技 2020-06-13 39 1

前言

建立型:单例模式,工厂模式,制作者模式,原型模式
结构型:桥接模式,署理模式,装饰器模式,适配器模式,门面模式,组合模式,享元模式
行为型:观察者模式,模板模式,计谋模式,责任链模式,状态模式,迭代器模式,接见者模式

先容

在事情中,我们经常要和Servlet Filter,Spring MVC Interceptor打交道,虽然我设置写的很6,然则出了问题还得四处google,于是看了一下源码,用Demo的方式来剖析一下这两者是怎么事情的。

Servlet Filter

Filter的使用

可能许多小伙伴没怎么用过Filter,我就简朴演示一下

1.在web.xml中设置2个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>

2.实现如下,略去了init方式和destroy方式

@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);
    }
}

3.然后你接见随便一个servlet方式,LogFilter和ImageFilter的doFilter方式都市执行

若是你在一个Filter方式后不加chain.doFilter(request, response)
则后续的Filter和Servlet都不会执行,这是为什么呢?看完我手写的Demo你一下就明了了

可以看到Filter可以在请求到达Servlet之前做处置,如

  1. 请求编码
  2. 敏感词过滤等

有兴趣的小伙伴可以看看相关的源码

手写Filter的实现

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 Interceptor

Interceptor的使用

以前写过一篇阻挡器应用的文章,有想领会使用方式的小伙伴可以看一下

用Spring MVC阻挡器做好web应用的安保措施

今天就来剖析一下阻挡器是怎么实现的?可以通过以下方式实现阻挡器

  1. 实现HandlerInterceptor接口
  2. 继续HandlerInterceptorAdapter抽象类,按需重写部门实现即可,(HandlerInterceptorAdapter也实现了HandlerInterceptor接口)

总而言之阻挡器必须必须实现了HandlerInterceptor接口

HandlerInterceptor有如下3个方式

boolean preHandler():在controller执行之前挪用
void postHandler():controller执行之后,且页面渲染之前挪用
void afterCompletion():页面渲染之后挪用,一样平常用于资源清算操作


这个图应该很好的显示了一个请求可以被阻挡的地方

  1. Servlet Filter是对一个请求到达Servlet的历程举行阻挡
  2. 而HandlerInterceptor是当请求到达DispatcherServlet后,在Controller的方式执行前后举行阻挡

手写Interceptor的实现

我来手写一个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可以有如下用处

  1. 纪录接口响应时间
  2. 判断用户是否上岸
  3. 权限校验等

可以看到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

,

诚信在线

诚信在线(www.mmsff.com)现已开放诚信在线手机版下载。游戏公平、公开、公正,用实力赢取信誉。

版权声明

本文仅代表作者观点,
不代表本站AllbetGaming的立场。
本文系作者授权发表,未经许可,不得转载。

评论

精彩评论
  • 2020-06-13 00:44:18

    Allbet开户欢迎进入Allbet开户(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。好多人都看啊