Spring之HandlerInterceptor和RequestBodyAdvice
一个请求在Spring中处理流程是有多种方式拦截处理的,而且,请求是可以拆分为进入和响应2个操作的,进入我们通常会对请求参数做处理,而响应我们通常会对响应参数做处理,Spring提供了多种方式给开发者。
一、HandlerInterceptor
我们写的controller,在Spring中被定义为handler,拦截controller的拦截器被定义为org.springframework.web.servlet.HandlerInterceptor。
拦截器的拦截逻辑是在org.springframework.web.servlet.DispatcherServlet中写的,需要注意的是,如果入口拦截顺序是a->b->c的话,那么出口拦截顺序是c->b->a,这个逻辑可以看org.springframework.web.servlet.HandlerExecutionChain里的一段逻辑。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;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);// Determine handler for the current request.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;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}}
这里能很清晰的看到循环使用的次序。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler, mv);}}
org.springframework.web.servlet.HandlerInterceptor
public interface HandlerInterceptor {/*** Interception point before the execution of a handler. Called after* HandlerMapping determined an appropriate handler object, but before* HandlerAdapter invokes the handler.* <p>DispatcherServlet processes a handler in an execution chain, consisting* of any number of interceptors, with the handler itself at the end.* With this method, each interceptor can decide to abort the execution chain,* typically sending an HTTP error or writing a custom response.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation returns {@code true}.* @param request current HTTP request* @param response current HTTP response* @param handler chosen handler to execute, for type and/or instance evaluation* @return {@code true} if the execution chain should proceed with the* next interceptor or the handler itself. Else, DispatcherServlet assumes* that this interceptor has already dealt with the response itself.* @throws Exception in case of errors*/default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/*** Interception point after successful execution of a handler.* Called after HandlerAdapter actually invoked the handler, but before the* DispatcherServlet renders the view. Can expose additional model objects* to the view via the given ModelAndView.* <p>DispatcherServlet processes a handler in an execution chain, consisting* of any number of interceptors, with the handler itself at the end.* With this method, each interceptor can post-process an execution,* getting applied in inverse order of the execution chain.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation is empty.* @param request current HTTP request* @param response current HTTP response* @param handler the handler (or {@link HandlerMethod}) that started asynchronous* execution, for type and/or instance examination* @param modelAndView the {@code ModelAndView} that the handler returned* (can also be {@code null})* @throws Exception in case of errors*/default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}/*** Callback after completion of request processing, that is, after rendering* the view. Will be called on any outcome of handler execution, thus allows* for proper resource cleanup.* <p>Note: Will only be called if this interceptor's {@code preHandle}* method has successfully completed and returned {@code true}!* <p>As with the {@code postHandle} method, the method will be invoked on each* interceptor in the chain in reverse order, so the first interceptor will be* the last to be invoked.* <p><strong>Note:</strong> special considerations apply for asynchronous* request processing. For more details see* {@link org.springframework.web.servlet.AsyncHandlerInterceptor}.* <p>The default implementation is empty.* @param request current HTTP request* @param response current HTTP response* @param handler the handler (or {@link HandlerMethod}) that started asynchronous* execution, for type and/or instance examination* @param ex any exception thrown on handler execution, if any; this does not* include exceptions that have been handled through an exception resolver* @throws Exception in case of errors*/default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}}
二、HandlerMethodArgumentResolver
上面的HandlerInterceptor可以清楚的看到接收的参数是HttpServletRequest,这是最早期的参数,紧接着Spring会从HttpServletRequest里把参数读取到controller定义的请求参数里面,此时用到的类型是HttpMessageConverter,他把参数写入controller中,此时是可以在参数写入前后做一些操作的。
三、RequestBodyAdvice
org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice
org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
这2个可以直接修改请求参数,可以看到写入之后,就得到了controller定义的参数类型。
public interface RequestBodyAdvice {/*** Invoked first to determine if this interceptor applies.* @param methodParameter the method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the selected converter type* @return whether this interceptor should be invoked or not*/boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked second before the request body is read and converted.* @param inputMessage the request* @param parameter the target method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the converter used to deserialize the body* @return the input request or a new instance (never {@code null})*/HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;/*** Invoked third (and last) after the request body is converted to an Object.* @param body set to the converter Object before the first advice is called* @param inputMessage the request* @param parameter the target method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the converter used to deserialize the body* @return the same body or a new instance*/Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked second (and last) if the body is empty.* @param body usually set to {@code null} before the first advice is called* @param inputMessage the request* @param parameter the method parameter* @param targetType the target type, not necessarily the same as the method* parameter type, e.g. for {@code HttpEntity<String>}.* @param converterType the selected converter type* @return the value to use, or {@code null} which may then raise an* {@code HttpMessageNotReadableException} if the argument is required*/@NullableObject handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);}
public interface ResponseBodyAdvice<T> {/*** Whether this component supports the given controller method return type* and the selected {@code HttpMessageConverter} type.* @param returnType the return type* @param converterType the selected converter type* @return {@code true} if {@link #beforeBodyWrite} should be invoked;* {@code false} otherwise*/boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);/*** Invoked after an {@code HttpMessageConverter} is selected and just before* its write method is invoked.* @param body the body to be written* @param returnType the return type of the controller method* @param selectedContentType the content type selected through content negotiation* @param selectedConverterType the converter type selected to write to the response* @param request the current request* @param response the current response* @return the body that was passed in or a modified (possibly new) instance*/@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response);}
四、整体时序
HandlerInterceptor preHandle ->
RequestBodyAdvice ->
HandlerMethodArgumentResolver ->
RequestBodyAdvice ->
controller ->
AOP afterReturning ->
ResponseBodyAdvice beforeBodyWrite ->
HttpMessageConverter(转JSON )->
HandlerInterceptor postHandle ->
HandlerInterceptor afterCompletion
相关文章:
Spring之HandlerInterceptor和RequestBodyAdvice
一个请求在Spring中处理流程是有多种方式拦截处理的,而且,请求是可以拆分为进入和响应2个操作的,进入我们通常会对请求参数做处理,而响应我们通常会对响应参数做处理,Spring提供了多种方式给开发者。 一、HandlerInte…...
transition、transform 区别和应用
先说应用 1.动画循环,复杂的动画用animation。在遇到很复杂的动画那就用animation。因为animation可以定义关键帧。那你就可以控制每一帧的动画效果。 2.简单的动画,事件触发用transition。当页面中的动画是自己执行的那么我们考虑用animation…...
Android中级——消息机制
消息机制 概念ThreadLocalMessageQueueLooperHandlerrunOnUiThread() 概念 MessageQueue:采用单链表的方法存储消息列表Looper:查询MessageQueue是否有新消息,有则处理,无则等待ThreadLocal:用于Handler获取当前线程的…...
【kubernetes】使用KubeSphere devops部署我的微服务系统
KubeSphere Devops 入门使用KubeSphere的Devops功能部署"我的微服务系统" (内容学习于尚硅谷云原生课程) kubesphere devops官方文档: https://v3-1.docs.kubesphere.io/zh/docs/devops-user-guide/how-to-use/create-a-pipeline-u…...
【哈士奇赠书活动 - 37期】- 〖深入浅出SSD:固态存储核心技术、原理与实战 第2版〗
文章目录 ⭐️ 赠书 - 《深入浅出SSD:固态存储核心技术、原理与实战 第2版》⭐️ 内容简介⭐️ 作者简介⭐️ 编辑推荐⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书 - 《深入浅出SSD:固态存储核心技术、原理与实战 第2版》 ⭐️ 内容简介 本书从基础认知、核心技…...
25.CSS自定义形状按钮与悬停效果
效果 源码 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>CSS Custom Shape Button</title><link rel="stylesheet" href="style.css"> </head> <body&…...
两条速度相差1350倍的sql语句
一起来学习研究下,看看是什么导致的这么大差别: SQL1: select * from RESOURCES where RES_STATUS 1 and PLATFORM_FLAG1 and RES_ID in (select RES_ID from DOWNLOADRECORDSwhere DOWNLOADRECORDS_TIME > DATE_SUB(now(),INTERVAL 7 DAY) grou…...
【UniApp开发小程序】小程序首页完善(滑到底部数据翻页、回到顶端、基于回溯算法的两列数据高宽比平衡)【后端基于若依管理系统开发】
文章目录 说明细节一:首页滑动到底部,需要查询下一页的商品界面预览页面实现 细节二:当页面滑动到下方,出现一个回到顶端的悬浮按钮细节三:商品分列说明优化前后效果对比使用回溯算法实现ControllerService回溯算法 优…...
使用errors.Wrapf()代替log.Error()
介绍不同语言的错误处理机制: Error handling patterns[1] Musings about error handling mechanisms in programming languages[2] 项目中 main调func1,func1调取func2... 这样就会出现很多的 if err ! nil { log.Printf()} , 在Kibana上查看时会搜到多条日志, 需要…...
企业面临的IP风险,如何应对?
IP风险画像为企业或组织在知识产权领域面临的潜在风险和威胁的综合概览。通过对相关知识产权的保护和管理,企业可以预测和应对潜在的法律、商业和声誉风险。 IP数据云帮助企业更好地了解和应对知识产权方面的风险。并提供了关于当前全球知识产权环境的重要信息&…...
L1-046 整除光棍(Python实现) 测试点全过
题目 这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由1组成的数字,比如1、11、111、1111等。传说任何一个光棍都能被一个不以5结尾的奇数整除。比如,111111就可以被13整除。 现在,你的程序要读入一个整数x,这个整…...
《Web安全基础》04. 文件上传漏洞
web 1:文件上传漏洞2:WAF 绕过2.1:数据溢出2.2:符号变异2.3:数据截断2.4:重复数据 本系列侧重方法论,各工具只是实现目标的载体。 命令与工具只做简单介绍,其使用另见《安全工具录》…...
文本匹配实战系列
引言 本系列文章开始介绍深度学习在文本匹配领域的应用,并且会尝试得到各种模型在给定的数据集上的表现。 深度文本匹配发展比较久,积累了很多文本匹配方法。也有很多的分类方式,一种分类方式是表示型和交互型。 表示型方法 表示型(repre…...
【Kafka】Kafka Stream简单使用
一、实时流式计算 1. 概念 一般流式计算会与批量计算相比较。在流式计算模型中,输入是持续的,可以认为在时间上是无界的,也就意味着,永远拿不到全量数据去做计算。同时,计算结果是持续输出的,也即计算结果…...
在Linux服务器上,查看系统最近的重启记录
在Linux服务器上,您可以查看系统的重启记录以了解系统何时进行了重启。系统的重启记录通常被记录在系统日志文件中。以下是在不同Linux发行版上查看系统重启记录的方法: 1. 使用 last 命令: 打开终端,并输入以下命令来查看系统的…...
Vue2023 面试归纳及复习
1. Vue 3中的Composition API(Hooks)是什么?它与Options API有何不同? 答:Composition API是Vue 3中引入的一种新的API风格, 用于组织和重用组件逻辑。它与Options API相比, 提供了更灵活和可…...
Android动态可编辑长度列表
概述 在界面实现一个列表,用户可以随意给列表新增或者删除项目,在开发中比较常用,但是真正做起来又有点花时间,今天花时间做一个,以便在以后的开发中用到。 详细 运行效果: 二、实现思路: 1…...
合并对象在 Typescript 中的实现与应用
合并对象在 Typescript 中的实现与应用 文章目录 合并对象在 Typescript 中的实现与应用一、简介二、实现1、函数实现2、参数说明3、返回值 三、使用示例四、实际应用场景五、拓展:使用 lodash-es 的 assign 函数进行对象合并1、简介2、安装与导入3、基础用法4、注意…...
antd upload组件beforeUpload返回promise之后,获取的文件不是file类型导致上传失败
之前的beforeUpload直接返回一个false值 ,文件是可以正常与服务端进行传输的 beforeUpload: (file) > {return false},但是这样并不能阻止文件上传,看了官方文档后,改用返回promise对象上传 beforeUpload: (file) > {console.log(-befo…...
创建ffmpeg vs2019工程
0 写在前面 本文主要参考链接:https://www.cnblogs.com/suiyek/p/15669562.html 感谢作者的付出; 1 目录结构 2 下载yasm和nasm 如果自己在安装VS2019等IDE的时候已经安装了它们,则不用再单独进行安装,比如我这边已经安装了&a…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
C++--string的模拟实现
一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现,其目的是加强对string的底层了解,以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量,…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...
工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...
RocketMQ 客户端负载均衡机制详解及最佳实践
延伸阅读:🔍「RocketMQ 中文社区」 持续更新源码解析/最佳实践,提供 RocketMQ 专家 AI 答疑服务 前言 本文介绍 RocketMQ 负载均衡机制,主要涉及负载均衡发生的时机、客户端负载均衡对消费的影响(消息堆积/消费毛刺等…...
