加载中...

[面试]HandlerInterceptors vs. Filters in Spring MVC

一. Filters

过滤器是web服务器的一部分,而不是Spring框架的一部分。对于传入的请求,我们可以使用过滤器来操作甚至阻止请求到达任何的servlet。反之亦然,我们也可以阻止响应到达客户端。

创建filter

我们需要新建一个类,实现javax.servlet.Filter interface

@Component
public class LogFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger(LogFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException {
        logger.info("Hello from: " + request.getLocalAddr());
        chain.doFilter(request, response);
    }

}

接下来,我们重写doFilter方法,在这里我们可以访问或操作ServletRequest、ServletResponse或FilterChain对象。我们可以使用FilterChain对象允许或阻止请求。
最后,我们通过@Component注释将Filter添加到Spring中。

二.HandlerInterceptors

HandlerInterceptors是Spring MVC框架的一部分,位于DispatcherServlet和我们的控制器之间。我们可以在请求到达控制器之前以及视图呈现之前和之后拦截请求。

创建HandlerInterceptors

要创建HandlerInterceptor,我们需要创建一个实现org.springframework.web.servlet.HandlerInterceptor接口的类。

  • preHandle() -在目标处理程序被调用之前执行
  • postHandle() -在目标处理程序之后但在DispatcherServlet呈现视图之前执行
  • afterCompletion() -在完成请求处理和视图呈现后回调
public class LogInterceptor implements HandlerInterceptor {

    private Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
      throws Exception {
        logger.info("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 
      throws Exception {
        logger.info("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
      throws Exception {
        logger.info("afterCompletion");
    }

}

三.关键区别和使用范例


过滤器在请求到达DispatcherServlet之前拦截请求,这使得它们非常适合执行粗粒度的任务,例如:

  • 身份验证
  • 日志记录和审计
  • 图像和数据压缩
  • 任何我们想从SpringMVC中解耦的功能
    HandlerIntercepors拦截DispatcherServlet和控制器之间的请求。这是在Spring MVC框架内完成的,提供了对处理程序和ModelAndView对象的访问

四.总结

区别 拦截器 过滤器
实现原理 基于Java的反射机制(动态代理) 基于函数回调
使用范围 是一个Spring组件,并由Spring容器管理,可以单独使用 实现 javax.servlet.Filter 接口,该接口在Servlet规范中定义,因此只能在web程序中使用
触发时机 请求进入servlet中,进入Controlloer之前预处理,Controlloer渲染了对应的视图之后请求结束 请求进入容器但进去servlet之前预处理,servlet处理完之后结束
拦截的请求范围 只对controller中请求或访问static目录下的资源请求起作用 对所有请求起作用

补充:

  1. 过滤器Filter基于函数回调?
    函数回调:A类拥有B类引用,通过A类完成业务时,对B进行实现,由调用方控制B的实现。相当于A调用了B,B中又调用了A的业务逻辑

举例:你去图书馆借两本书,但是只有一本有,另外一本被别人借走了,于是你就先借了一本,跟管理员说,等第二本到了,跟你打电话,你来拿。
上面的过程就相当于一个回调函数,你是回调对象,管理员是控制器对象,打电话是回调的方法。

引自https://blog.csdn.net/weixin_44036154/article/details/108220620

@WebFilter("/*")  //对所有的请求都执行该过滤器
public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    //执行过滤处理
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {

        req.setCharacterEncoding("UTF-8");

        //放行
        filterChain.doFilter(req,resp);
    }

    @Override
    public void destroy() {
    }
}

在调用 doFilter 方法时,会传递一个 filterChain 对象进来,如果调用该方法,则 web 服务器就会调用 web 资源的 service 方法,即 web 资源就会被访问。

  1. 拦截器?
    我们创建一个计算方法执行时间的拦截器
@Slf4j
public class MyHandlerInterceptor implements HandlerInterceptor {

    // 进入我们的 Controller方法之前执行,如果返回结果为false,那么的处理器就不执行了。如果为true,放行。
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("==================preHandle====================");
//        String path = request.getServletPath();
//        System.out.println(handler);
//        System.out.println(path);
        Long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        log.info(handler + "被调用了");
        return true;
    }

    // 我们的处理器执行完毕,在返回 modelAndView 给前端控制器之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        System.out.println("==================postHandle====================");
        Long startTime = (Long)request.getAttribute("startTime");
        Long endTime = System.currentTimeMillis();
        log.info("执行时间为:" + (endTime-startTime)/1000 + "秒");
    }

    // 把 modelAndView 返回给前端控制器之后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        System.out.println("==================afterCompletion====================");
    }
}

流程如下:

我们从流程图可以看到,拦截器的执行是在穿插在SpringMvc的工作流程中的,并没有用到动态代理机制,直接调用的拦截器方法。
拦截器在实现层面,并没有用到aop,并没有切面,通知这一类的代码,所以它的实现并不是基于aop的。但是拦截器从思想层面上,是面向切面编程的,是在controller这个层面上进行的代码织入。

引自https://blog.csdn.net/oracle1158/article/details/123307319

posted @ 2022-09-01 13:23  Vincy9501  阅读(21)  评论(0编辑  收藏  举报