• 过滤器和拦截器的辨析


    过滤器和拦截器的辨析

    介绍#

    过滤器和拦截器都是为了在请求到达目标处理器(Servlet或Controller)之前或者之后插入自定义的处理逻辑

    • 过滤器:

    遵循AOP(面向切面编程)思想实现,基于Servlet规范提供的Filter接口,它是位于客户端请求与服务器响应之间的一个组件,依赖于Servlet容器。当请求到达服务器时,过滤器会在请求进入实际目标资源(如Servlet、JSP页面)之前或之后执行特定的操作,原理是基于函数回调

    • 拦截器

    遵循AOP(面向切面编程)思想实现,如Spring MVC中的HandlerInterceptor接口,它不依赖于Servlet容器的具体实现,而是由应用框架管理。拦截器是在请求进入到控制器层(Controller)方法前后执行自定义逻辑

    原理解析#

    过滤器#

    为什么说过滤器基于函数回调?

    过滤器基于函数回调,所谓函数回调/回调函数指的是:一个函数(称为回调函数)作为参数传递给另一个函数(称为调用函数),当满足一定条件或者在某个特定时刻,调用函数会调用传递过来的回调函数

    由于Java中不直接支持函数指针,所以常常通过接口来实现回调机制

    FilterChain就是一个接口

    public interface FilterChain {
    
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException;
    
    }
    

    Filter的实现类中doFilter()方法中FilterChain作为参数被传进来,并且在合适的时机被回调了其doFilter方法

    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            Filter.super.init(filterConfig);
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            log.info("hello");
            chain.doFilter(request,response);
        }
    
        @Override
        public void destroy() {
            Filter.super.destroy();
        }
    }
    

    拦截器#

    拦截器基于AOP(面向切面编程)思想实现,但是并不一定用到动态代理或者切面,切点之类的技术,以如Spring MVC中的HandlerInterceptor接口为例,从源码看更像是直接将拦截器注入,形成了一个拦截器链,在controller层面上进行代码织入

    DispatcherServlet作为SpringMVC框架的核心类,http请求的核心执行方法为doService(),再进入doDispatch()方法

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		HttpServletRequest processedRequest = request;
            /**
             * 1.
             * HandlerExecutionChain 是一个对象
             * 包含了以下重要的属性
             * private final Object handler; //处理器(controller和其最后的方法)
             * List interceptorList = new ArrayList<>();//拦截器列表,用来存储匹配处理器的拦截器
             */
    		HandlerExecutionChain mappedHandler = null;
    		boolean multipartRequestParsed = false;
    
    		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
    		try {
    			ModelAndView mv = null;
    			Exception dispatchException = null;
    
    			try {
    				processedRequest = checkMultipart(request);
    				multipartRequestParsed = (processedRequest != request);
    
                    /**
                     * 2.
                     * 下面这行代码 大概做了以下事情
                     * 1.通过request和url 匹配了对应的controller以及调用的方法 填充了HandlerExecutionChain.handler
                     * 2.通过匹配request和HandlerInterceptor的注册信息(拦截哪些,放行哪些),往HandlerExecutionChain.interceptorList中添加对应的拦截器
                     */
    				mappedHandler = getHandler(processedRequest);
    				if (mappedHandler == null) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				// Determine handler adapter for the current request.
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// Process last-modified header, if supported by the handler.
    				String method = request.getMethod();
    				boolean isGet = HttpMethod.GET.matches(method);
    				if (isGet || HttpMethod.HEAD.matches(method)) {
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
                    /**
                     * 3.执行 拦截器链条中的所有前置方法
                     */
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					return;
    				}
    
                    /**
                     * 4.交由处理器(controller对应的方法)去处理方法中的业务逻辑
                     */
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    
    				applyDefaultViewName(processedRequest, mv);
                    
                    /**
                     * 5.倒序执行 拦截器链条中的所有后置方法
                     */
    				mappedHandler.applyPostHandle(processedRequest, response, mv);
    			}}
            //...
    

    流程解析#

    由于Filter依赖于Servlet容器所以不同的容器Filter,FilterChain的实现类存在差异,这里以Tomcat为例分析

    1.向后台发起一次请求

    2.接待线程接收到,将任务转交给工作线程

    3.判断协议,封装必要对象

    4.将request,response一路转交至StandardWrapperValve.invoke(Request request, Response response)

    5.创建过滤链ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

    ​ 5.1从上下文中获取注册好的过滤器

    ​ 5.2遍历过滤器,匹配URL,Servlet等,将匹配好的过滤器加入到过滤器链

    6.依次回调FilterChain的doFilter方法filterChain.doFilter(request.getRequest(),response.getResponse());

    7.将所有过滤器的前置代码执行完毕,进入servlet,servlet.service(request, response);

    8.进入DispatcherServlet统一调度

    9.调用拦截器前置方法

    10.进入controller中的对应方法,执行具体的业务逻辑

    11.调用拦截器后置(数组倒序执行)

    12.将所有过滤器的后置代码执行完毕(方法栈,先进后出)

    13.将结果返回给请求者

    注意,过滤器和拦截器实现先进后出的实现方式是不同的,过滤器基于函数回调,方法栈结构天生支持先进后出;拦截器则是直接使用循环倒序遍历

    总结#

    同:

    • 过滤器和拦截器都遵循面向切面编程的思想(AOP),实现了在请求到达目标处理器(Servlet/Controller)之前或者之后插入自定义的处理逻辑

    异:

    • 使用范围不同

      过滤器实现的是javax.servlet.Filter该接口在Servlet规范中定义,依赖WEB容器

      拦截器是一个Spring组件,由Spring管理,并不依赖Tomcat容器,可以单独使用(Application,Swing)

    • 使用的场景不同

      拦截器更加接近业务系统,所以拦截器更适用于处理统一的业务逻辑,比如权限判断等

      过滤器通常用来实现通用功能,比如xss过滤,敏感词,处理跨域等等

    • 触发的时机不同

      过滤器的触发时机早于拦截器

    • 底层实现细节不同

      过滤器实现先进后出基于方法栈的数据结构

      拦截器实现先进后出基于循环倒序遍历

  • 相关阅读:
    Python 简介
    Django教程
    Mysql 索引
    python数据分析小案例:把招聘数据做可视化处理~
    [2023.09.12]: Yew应用开发的第一个hook--use_state
    阿里云K8S部署Go+Vue项目
    红蓝对抗-最全信息收集工具
    高等数学(第七版)同济大学 习题7-4 个人解答
    Elasticsearch - Elasticsearch集群Cluster(三)
    【附源码】计算机毕业设计SSM网课信息管理系统
  • 原文地址:https://www.cnblogs.com/void-cmy/p/18065471