当前位置:首页 > 科技  > 软件

SpringBoot拦截器和动态代理有什么区别?

来源: 责编: 时间:2023-09-18 21:41:16 447观看
导读在 Spring Boot 中,拦截器和动态代理都是用来实现功能增强的,所以在很多时候,有人会认为拦截器的底层是通过动态代理实现的,所以本文就来盘点一下他们两的区别,以及拦截器的底层实现。1、拦截器拦截器(Interceptor)准确来说

Qjr28资讯网——每日最新资讯28at.com

在 Spring Boot 中,拦截器和动态代理都是用来实现功能增强的,所以在很多时候,有人会认为拦截器的底层是通过动态代理实现的,所以本文就来盘点一下他们两的区别,以及拦截器的底层实现。Qjr28资讯网——每日最新资讯28at.com

1、拦截器

拦截器(Interceptor)准确来说在 Spring MVC 中的一个很重要的组件,用于拦截 Controller 的请求。它的主要作用有以下几个:Qjr28资讯网——每日最新资讯28at.com

  • 权限验证:验证用户是否登录、是否有权限访问某个接口。
  • 日志记录:记录请求信息的日志,如请求参数,响应信息等。
  • 性能监控:监控系统的运行性能,如慢查询接口等。
  • 通用行为:插入一些通用的行为,比如开发环境忽略某些请求。

典型的使用场景是身份认证、授权检查、请求日志记录等。Qjr28资讯网——每日最新资讯28at.com

(1)拦截器实现

在 Spring Boot 中拦截器的实现分为两步:Qjr28资讯网——每日最新资讯28at.com

  • 创建一个普通的拦截器,实现 HandlerInterceptor 接口,并重写接口中的相关方法。
  • 将上一步创建的拦截器加入到 Spring Boot 的配置文件中,并配置拦截规则。

具体实现如下。Qjr28资讯网——每日最新资讯28at.com

实现自定义拦截器

import org.springframework.stereotype.Component;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Componentpublic class TestInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        System.out.println("拦截器:执行 preHandle 方法。");        return true;    }    @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {        System.out.println("拦截器:执行 postHandle 方法。");    }    @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        System.out.println("拦截器:执行 afterCompletion 方法。");    }}

其中:Qjr28资讯网——每日最新资讯28at.com

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handle):在请求方法执行前被调用,也就是调用目标方法之前被调用。比如我们在操作数据之前先要验证用户的登录信息,就可以在此方法中实现,如果验证成功则返回 true,继续执行数据操作业务;否则就返回 false,后续操作数据的业务就不会被执行了。
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handle,ModelAndView modelAndView):调用请求方法之后执行,但它会在 DispatcherServlet 进行渲染视图之前被执行。
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex):会在整个请求结束之后再执行,也就是在 DispatcherServlet 渲染了对应的视图之后再执行。

配置拦截规则

然后,我们再将上面的拦截器注入到项目配置文件中,并设置相应拦截规则,具体实现代码如下:Qjr28资讯网——每日最新资讯28at.com

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configurationpublic class AppConfig implements WebMvcConfigurer {    // 注入拦截器    @Autowired    private TestInterceptor testInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {        registry.addInterceptor(testInterceptor) // 添加拦截器                .addPathPatterns("/**"); // 拦截所有地址          .excludePathPatterns("/login"); // 放行接口    }}

这样我们的拦截器就实现完了。Qjr28资讯网——每日最新资讯28at.com

(2)拦截器实现原理

Spring Boot 拦截器是基于 Java 的 Servlet 规范实现的,通过实现 HandlerInterceptor 接口来实现拦截器功能。Qjr28资讯网——每日最新资讯28at.com

在 Spring Boot 框架的执行流程中,拦截器被注册在 DispatcherServlet 的 doDispatch() 方法中,该方法是 Spring Boot 框架的核心方法,用于处理请求和响应。Qjr28资讯网——每日最新资讯28at.com

程序每次执行时都会调用 doDispatch() 方法时,并验证拦截器(链),之后再根据拦截器返回的结果,进行下一步的处理。如果返回的是 true,那么继续调用目标方法,反之则会直接返回验证失败给前端。Qjr28资讯网——每日最新资讯28at.com

doDispatch  源码实现如下:Qjr28资讯网——每日最新资讯28at.com

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {    HttpServletRequest processedRequest = request;    HandlerExecutionChain mappedHandler = null;    boolean multipartRequestParsed = false;    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);    try {        try {            ModelAndView mv = null;            Object dispatchException = null;            try {                processedRequest = this.checkMultipart(request);                multipartRequestParsed = processedRequest != request;                mappedHandler = this.getHandler(processedRequest);                if (mappedHandler == null) {                    this.noHandlerFound(processedRequest, response);                    return;                }                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());                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;                    }                }                // 调用预处理【重点】                if (!mappedHandler.applyPreHandle(processedRequest, response)) {                    return;                }                // 执行 Controller 中的业务                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());                if (asyncManager.isConcurrentHandlingStarted()) {                    return;                }                this.applyDefaultViewName(processedRequest, mv);                mappedHandler.applyPostHandle(processedRequest, response, mv);            } catch (Exception var20) {                dispatchException = var20;            } catch (Throwable var21) {                dispatchException = new NestedServletException("Handler dispatch failed", var21);            }            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);        } catch (Exception var22) {            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);        } catch (Throwable var23) {            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));        }    } finally {        if (asyncManager.isConcurrentHandlingStarted()) {            if (mappedHandler != null) {                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);            }        } else if (multipartRequestParsed) {            this.cleanupMultipart(processedRequest);        }    }}

从上述源码可以看出在开始执行 Controller 之前,会先调用 预处理方法 applyPreHandle,而 applyPreHandle 方法的实现源码如下:Qjr28资讯网——每日最新资讯28at.com

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {        // 获取项目中使用的拦截器 HandlerInterceptor        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);        if (!interceptor.preHandle(request, response, this.handler)) {            this.triggerAfterCompletion(request, response, (Exception)null);            return false;        }    }    return true;}

从上述源码可以看出,在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执行拦截器中的 preHandle 方法,这样就会咱们前面定义的拦截器对应上了,如下图所示:Qjr28资讯网——每日最新资讯28at.com

Qjr28资讯网——每日最新资讯28at.com

此时用户登录权限的验证方法就会执行,这就是拦截器的执行过程。因此,可以得出结论,拦截器的实现主要是依赖 Servlet 或 Spring 执行流程来进行拦截和功能增强的。Qjr28资讯网——每日最新资讯28at.com

2、动态代理

动态代理是一种设计模式,它是指在运行时提供代理对象,来扩展目标对象的功能。在 Spring 中的,动态代理的实现手段有以下两种:Qjr28资讯网——每日最新资讯28at.com

  • JDK 动态代理:通过反射机制生成代理对象,目标对象必须实现接口。
  • CGLIB 动态代理:通过生成目标类的子类来实现代理,不要求目标对象实现接口。

动态代理的主要作用包括:Qjr28资讯网——每日最新资讯28at.com

  • 扩展目标对象的功能:如添加日志、验证参数等。
  • 控制目标对象的访问:如进行权限控制。
  • 延迟加载目标对象:在需要时才实例化目标对象。
  • 远程代理:将请求转发到远程的目标对象上。

Qjr28资讯网——每日最新资讯28at.com

JDK 动态代理和 CGLIB 的区别详见:www.javacn.site/interview/spring/jdk_cglib.htmlQjr28资讯网——每日最新资讯28at.com

Qjr28资讯网——每日最新资讯28at.com

3、拦截器 VS 动态代理

因此,我们可以得出结论,拦截器和动态代理虽然都是用来实现功能增强的,但二者完全不同,他们的主要区别体现在以下几点:Qjr28资讯网——每日最新资讯28at.com

  • 使用范围不同:拦截器通常用于 Spring MVC 中,主要用于拦截 Controller 请求。动态代理可以使用在 Bean 中,主要用于提供 bean 的代理对象,实现对 bean 方法的拦截。
  • 实现原理不同:拦截器是通过 HandlerInterceptor 接口来实现的,主要是通过 afterCompletion、postHandle、preHandle 这三个方法在请求前后进行拦截处理。动态代理主要有 JDK 动态代理和 CGLIB 动态代理,JDK 通过反射生成代理类;CGLIB 通过生成被代理类的子类来实现代理。
  • 加入时机不同:拦截器是在运行阶段动态加入的;动态代理是在编译期或运行期生成的代理类。
  • 使用难易程度不同:拦截器相对简单,通过实现接口即可使用。动态代理稍微复杂,需要了解动态代理的实现原理,然后通过相应的 api 实现。

小结

在 Spring Boot 中,拦截器和动态代理都是用来实现功能增强的,但二者没有任何关联关系,它的区别主要体现在使用范围、实现原理、加入时机和使用的难易程度都是不同的。Qjr28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-10450-0.htmlSpringBoot拦截器和动态代理有什么区别?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Go并发可视化解释 – select语句

下一篇: Serverless vs Containers:哪个适合您的业务?

标签:
  • 热门焦点
  • 卢伟冰长文解析K60至尊版 对Redmi有着里程碑式的意义

    在今天的Redmi后性能时代战略发布会结束之后,Redmi总经理卢伟冰又带来了一篇长文,详解了为什么 Redmi 要开启后性能时代?为什么选择和 MediaTek、Pixelworks 深度合作?以及后性
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • 直屏旗舰来了 iQOO 12和K70 Pro同台竞技

    旗舰机基本上使用的都是双曲面屏幕,这就让很多喜欢直屏的爱好者在苦等一款直屏旗舰,这次,你们等到了。据博主数码闲聊站带来的最新爆料称,Redmi下代旗舰K70 Pro和iQOO 12两款手
  • 2023年Q2用户偏好榜:12+256G版本成新主流

    3月份的性能榜、性价比榜和好评榜之后,就要轮到2023年的第二季度偏好榜了,上半年的新机潮已经过去,最明显的肯定就是大内存和存储的机型了,另外部分中端机也取消了屏幕塑料支架
  • 6月iOS设备好评榜:第一蝉联榜首近一年

    作为安兔兔各种榜单里变化最小的那个,2023年6月的iOS好评榜和上个月相比没有任何排名上的变化,仅仅是部分设备好评率的下降,长年累月的用户评价和逐渐退出市场的老款机器让这
  • 企业采用CRM系统的11个好处

    客户关系管理(CRM)软件可以为企业提供很多的好处,从客户保留到提高生产力。  CRM软件用于企业收集客户互动,以改善客户体验和满意度。  CRM软件市场规模如今超过580
  • 十个简单但很有用的Python装饰器

    装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为。装饰器本质上是一个函数,它接受另一个函数或类作为参数,并返回一个新的函数或类。它们通常用
  • 共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top