过滤器和拦截器

过滤器:Filter

描述:Filter对web服务器管理的所有资源进行拦截,例如实现URL级别的权限访问控制、过滤敏感词汇等。

大致流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter接口中有一个doFilter方法,里面编写我们的业务逻辑,配置对哪个资源进行拦截,在调用service方法之前,都会先调用一下filter的doFilter方法,也可以编写多个Filter,这些Filter组合起来称之为一个Filter链,也可以控制先调用哪个Filter。web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

public void init(FilterConfig filterConfig) //初始化

Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,
完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) //这个方法完成实际的过滤操作。
public void destroy() //销毁

OncePerRequestFilter

Spring的OncePerRequestFilter类实际上是一个实现了Filter接口的抽象类。spring对Filter进行了一些封装处理,能够确保在一次请求只通过一次filter,
而不需要重复执行,因为在执行完后会在request设置标识符。

@Override
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
        throw new ServletException("OncePerRequestFilter just supports HTTP requests");
    }
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    //得到一个固定标识,这个标识的作用是标记:该过滤器是否被执行过。也就是这个来实现执行一次的操作。
    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

    //检测当前请求是否已经拥有了该标记,如果拥有该标记则代表该过滤器执行过了
    boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;

    //如果执行过,或者不在拦截范围直接跳过,
    if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || shouldNotFilter(httpRequest)) {
        filterChain.doFilter(request, response);
    }else {
        //往request设置标识。
        request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
        try {
            //这个方法是一个抽象方法需要子类去实现具体的过滤逻辑。
            doFilterInternal(httpRequest, httpResponse, filterChain);
        }finally {
            //移除该标记。什么时候会执行到这里?
            request.removeAttribute(alreadyFilteredAttributeName);
        }
    }
}

那么doFilterInternal应该做什么?

public class xxxx extends OncePerRequestFilter {
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain){
        //执行业务代码
		//根据需要是否调用doFilter
        filterChain.doFilter(requestToUse, response);
    }
}

所以回到上面为什么会在finally里面调用request.removeAttribute() 也就是在请求接口最后,在执行移除标识操作。

拦截器:

SpringBoot之HandlerInterceptorAdapter
在SpringBoot中我们可以使用HandlerInterceptorAdapter这个适配器来实现自己的拦截器。这样就可以拦截所有的请求并做相应的处理。
应用场景:请求日志,权限,JVM性能输出等。

在HandlerInterceptorAdapter中主要提供了以下的方法:

  1. preHandle:在方法被调用前执行。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行。
  2. postHandle:在方法执行后调用。
  3. afterCompletion:在整个请求处理完毕后进行回调,也就是调用方已经拿到响应时。

拦截器和过滤器的区别:

  • 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
  • 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  • 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  • 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  • 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  • 拦截器可以获取ioc中的service bean实现业务逻辑,拦截器可以获取ioc中的service
    bean实现业务逻辑,在拦截器里注入一个service,可以调用业务逻辑。

触发时机:
过滤器是在请求进入容器后,但请求进入servlet之前进行预处理的。请求结束返回也是,是在servlet处理完后,返回给前端之前。
总结:过滤器包裹住servlet,servlet包裹住拦截器

过滤器的触发时机是容器后,servlet之前,所以过滤器的
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
的入参是ServletRequest ,而不是httpservletrequest。因为过滤器是在httpservlet之前。

过滤器

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
    System.out.println("before...");
    chain.doFilter(request, response);
	System.out.println("after...");
}

chain.doFilter(request, response);这个方法的调用作为分水岭。事实上调用Servlet的doService()方法是在chain.doFilter(request, response);这个方法中进行的。

2.拦截器是被包裹在过滤器之中的。
拦截器的preHandle()方法是在过滤器的chain.doFilter(request, response)方法的前一步执行,不是doFilter(request,response,chain)之前。
postHandle()方法在return ModelAndView之前,可以操控Controller的内容。
afterCompletion()方法是在过滤器返回给前端前一步执行。也就是doFilter(request,response,chain)方法return之前执行。
在我们项目中用的最多是preHandle这个方法,而未用其他的,框架提供了一个已经实现了拦截器接口的适配器类HandlerInterceptorAdapter,继承这个类重写一下需要用到的方法就行了,可以少几行代码,这种方式Java中很多地方都有体现。

本文摘自:
https://www.cnblogs.com/panxuejun/p/7715917.html
https://www.cnblogs.com/weianlai/p/11358768.html
https://blog.csdn.net/u012554102/article/details/51462161
https://www.cnblogs.com/cainiao-chuanqi/p/11326793.html

上一篇:JavaWeb知识小汇(9)——Filter


下一篇:6.Apache Kylin 查询报错 Coprocessor resource limit exceeded: scanned bytes exceeds threshold