范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

一个error引发两小时的SpringMVC源码debug

  前言
  最近入职新公司,先临时接手一个认证项目,对于本人这种有代码优雅强迫症的,看到不爽的代码毫无疑问就是改!改!改!然而改完之后前端给我反馈了接口总是报 401 错误。我的内心:我草?难道是我改出 bug 了?不应该吧,这么简单的东西怎么会有 bug !于是我自己测试了下,还真是有问题,但不是我的问题,下面开始分析!伪代码场景还原
  登录接口,模拟报错@PostMapping("/user/login") public LoginResult login(@RequestBody LoginRequest request) {     throw new RuntimeException("模拟登录接口报错"); }
  接着贴出拦截器,如果需要认证的请求没有携带 token ,或者 redis 中查不到该 token 相关用户,就抛出异常public class UserLoginInterceptor implements HandlerInterceptor {     @Override     public boolean preHandle( HttpServletRequest request, HttpServletResponse response, Object handler) {         String token = request.getHeader("token");         if (token == null) {             throw new UnauthorizedException("未认证或token已过期");         } else {             if(redis.get(token) == null) {                 throw new UnauthorizedException("未认证或token已过期");             }             //...将token和用户信息设置到 ThreadLocal         }         return true;     } }
  拦截器配置@Configuration public class WebConfig implements WebMvcConfigurer {     @Override     public void addInterceptors(InterceptorRegistry registry) {         registry.addInterceptor(new UserLoginInterceptor())                 .excludePathPatterns("/user/login")                 .addPathPatterns("/**");     } }
  这个项目是通过拦截器中获取 token 去 Redis 查用户信息放到 ThreadLocal 里面的,由于一个请求从 Controller → Service → Mapper 线程 ID 都是一致的,这样一条请求链都能从这个 ThreadLocal 里面拿到当前登录用户信息。可以看到 /user/login 是被拦截器放行的,然而当这个请求的 Controller 报错,预期的 message 信息应该是 模拟登录接口报错,然而运行时报的居然是下面未认证的错,这说明我们的请求走到了拦截器{     "path": "/error",     "message": "com.yinshan.auth.core.exception.UnauthorizedException: 未认证或token已过期",     "error": "Unauthorized",     "status": 401,     "timestamp": "2021-09-22T14:03:39.986559500" }
  当然这个错误信息格式是我自己处理过的,这个不重要,重点是我在登录接口中报 500 的错,为啥变成了拦截器中的 401 未认证。调试分析
  废话少说,直接 debug 走起,在抛异常的代码上打个断点,再把拦截器中打个断点
  结果在登录接口按下 F9 之后,断点确实走到了拦截器中,
  说实话我当时真的是这个表情,这特么已经被拦截器放行的接口报错关拦截器什么事?然而在调试面板仔细一看 preHandle 这个方法的请求参数详细信息发现了猫腻。
  图中箭头指向是很重要的信息:是当前请求的上下文,正常请求走拦截器时是没有这个上下文请求的分发类型,正常请求的值是 REQUEST特别显眼的是这个请求资源 uri,根本不是我请求的 /user/login,而是一个 /error
  看到这里大致就明白了,这个断点走到拦截器,不是因为 /user/login 这个请求,而是另一个 /error 请求。那么这个 /error 是怎么来的?由于图中的 TomcatEmbededContext 上下文是 SpringBoot 内嵌的 Tomcat 中的一个类,我猜这个请求应该是 SpringMVC 控制器遇到未处理的报错重新内部发起的一个 /error 请求。
  也许你会疑惑,这不是找到问题了吗?好像挺快的呀,你为啥搞了两个小时呢?因为我菜啊! 我调试的时候压根就没关心这个参数是啥,而是一步一步 F8 → F7 → F8 → F7 ...... 过五关斩六将…最后调试到了 DispatchServlet 的时候我才反应过来,这特么怎么跑到请求转发了,最后终于明白了,人都麻了。查询官方文档
  果然在 SpringMVC 的官方文档找到了说明
  官网说的很清楚了,如果异常没有被默认的异常处理器处理,那么 Servlet 容器将会用 DispatchServlet 分派一个 /error 请求,也可以对 /error 请求进行定制化处理,详情可以参考 SpringMVC 官方文档具体原因
  SpringMVC 的控制器报错之后服务器会弄一个 /error 的请求,由于我们的拦截器没有放行这个 /error 请求,所以会在 DispatchServlet 中执行该请求的拦截器(我突然想起两年前还写过自定义 SpringBoot 异常页面,就是处理的 /error 请求)protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {     //...     //判断并执行拦截器的 preHandle()     if (!mappedHandler.applyPreHandle(processedRequest, response)) {        return;     }
  上面是 DispatchServlet 的 doDispatch 部分源码,我相信大多数人对于 doDispatch 的理解都停留在为了面试,背 SpringMVC 执行流程的时候。其实网上对于 SpringMVC 执行流程画的图都是几个关键节点,并没有这么细致,如果说没有真正带着问题调试过这段源码,那么大概率也是不懂这个问题的。解决方案
  明白了问题的原因,解决就很简单了。只要在我们自定义的认证拦截器中排除掉对 /error 的拦截即可@Override public void addInterceptors(InterceptorRegistry registry) {     registry.addInterceptor(new UserLoginInterceptor())             .excludePathPatterns("/error").addPathPatterns("/**"); } 谈谈拦截器
  其实上面的问题很大一部分都是因为对拦截器没有真正的理解,只是知道它能够拦截一个请求,而没有研究过它在什么阶段拦截,在 SpringMVC 中又是怎么去实现的。那么接下来深入分析一下拦截器拦截器与过滤器的使用范围
  查看 Filter 接口源码就能发现,它是 javax.servlet 包下的,而 HandlerInterceptor 是 org.springframework.web.servlet 包下的,拦截器是 SpringMVC 实现的,实际上它只是一个或者多个 Java 类组合实现拦截而已,和 web 应用没有必然联系。这意味着过滤器只能在 web 应用中使用,而拦截器可以用在任何可以用 Spring 和 SpringMVC 的地方,比如桌面应用程序。拦截器和过滤器的执行顺序&执行流程
  过滤器的执行是在请求到达 Servlet 之前通过 ApplicationFilterChain.doFilter() 进行链式调用的,在 doFilter() 内部获取到下一个过滤器实例,执行过滤方法,它的执行顺序是 filter1 → ApplicaitonFilterChain.doFilter() → filter2 → ApplicationFilterChain.doFilter() → filter3 → ......
  如下图
  而拦截器的执行是请求到达 DispatchServlet 之后针对 Controller 方法执行前、执行后做的一些事情,如下图,这里的过滤器链就是上面那张图
  很明显 preHandle() 才是拦截的关键,只有它是在请求到达 Controller 目标方法之前执行的,该方法通过返回 true/false 决定请求是否需要被拦截。doDispatch 内部对拦截器的处理部分源码
  我们都知道 DispatchServlet 的 doDispatch() 方法是处理所有请求的,内部和拦截器相关的代码如下//调用 Controller 目标方法前执行拦截器的 preHandle() if (!mappedHandler.applyPreHandle(processedRequest, response)) {     return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//反射调用 Controller 目标方法 /**  * ...省略  * */ mappedHandler.applyPostHandle(processedRequest, response, mv);//Controller 目标方法执行完后调用拦截器 postHandle() //请求完成之后执行拦截器的 afterCompletion() processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  其实真正带着问题调试过源码的话,根本就不需要背 SpringMVC 的执行流程面试题啦~~~ 我就背不下来,但是从源码调试过程中,我已经很清楚了 DispatchServlet 在请求转发过程中都做了那些事情,结合之前说过的 参数校验神器 hibernate-validator 配合统一异常处理 自然也明白了 SpringMVC 是怎样实现请求参数的解析、转换的。结语
  遇到问题不要慌,源码调试没有那么难,我觉得带着问题去看源码更能够让印象更深刻。来新公司不到一个月,我已经带着问题看了好几次源码了......正好赶上换技术组件的大版本,总是有各种奇奇怪怪的问题。
  平时多看看框架、技术组件的官方文档真的是一个非常好的习惯,不要总局限于某些视频教程。多读官方文档,才能发现组件可能存在的问题,出现问题的原因。
  作者:暮色妖娆丶
  链接:https://juejin.cn/post/7013636541566156813

促进算法推荐技术规范健康发展近年来,随着科技不断发展,算法推荐技术已被广泛应用于生活。算法推荐技术通过抓取用户日常的使用数据,分析得出人们的行为习惯和喜好,进而精准化地提供信息娱乐消费等各类服务。算法推荐技术隐私安全保护再加强!苹果要求APP必须要有删除账号功能智能手机方便了人们的工作和生活各个方面,但是在无形之中,智能手机也出现了很多的安全隐患,而这其中最严重的一点就是隐私安全问题,比如某些流氓软件,它会收集用户的敏感信息!对于用户来说约克水系统中央空调,家用中央空调的明智之选约克水生态中央空调带地暖一百多年前人类发明空调以来,一直在不断追求舒适健康的生活方式。家用水系统中央空调以它独特的舒适健康节能安全以及智能完美契合未来智慧建筑的品质需求。约克水系统京东到家发布年货轻指南空气炸锅等销售额同比增超10倍新京报贝壳财经讯(记者程子姣)1月24日,达达集团研究院根据京东小时购京东到家消费数据,发布2022年货消费轻指南,数据显示,自1月7日京东到家小时购年货节开启以来,京东小时购京东独家吉利力帆合资公司定名睿蓝枫叶汽车或退出市场财联社(北京,记者刘阳)讯,在换电模式的风口中,吉利汽车与力帆科技的换电出行业务迈出了实质性的一步。1月24日,吉利汽车公告,与力帆科技以5050股比设立的合资公司,已完成工商注册谈判五年仍陷僵局特斯拉折戟印度市场?21世纪经济报道记者彭苏平上海报道销量成绩如日中天的特斯拉,进入印度市场的时间却一延再延。近日,又有网友在社交网络平台上提问,特斯拉何时在印度推出电动汽车?特斯拉CEO伊隆马斯克(没有华为,中国智能手机市场的均价都降了?众所周知,自从华为MateP系列手机大火之后,很多人就吐槽华为手机太贵了,因为与苹果相比,华为MateP系列的价格也不遑多让。那么问题就来了,2021年开始,华为手机销量持续下滑,长安新能源公布最新产品规划,包含5款新车长安新能源官方公布了最新的产品规划,总共包含5款新车,A158将于34月发布,67月交付。此外,长安还公布了EPA1平台电动技术智能技术以及全新的服务。据悉,目前还有三款基于EPA强大!我国自研SpaceOS天卓操作系统正式发布IT之家1月24日消息,说起我国的航天科技,那是杠杠的。不过关于我国航天技术背后的很多细节,相信不少朋友并不知晓,比如航天器计算机运行的操作系统。而根据中国航天报官方的消息,我国自我国绿色交通十四五发展规划发布加快推进新能源汽车推广应用来源人民网人民网北京1月24日电(记者乔雪峰)近日,交通运输部印发绿色交通十四五发展规划(以下简称规划),规划提出,到2025年,交通运输领域绿色低碳生产方式初步形成,基本实现基础国务院加快绿色仓储建设2025年新能源汽车新车销售量达20观点网讯1月24日,国务院发布关于印发十四五节能减排综合工作方案的通知。据观点新媒体了解,方案指出,全面推进城镇绿色规划绿色建设绿色运行管理,推动低碳城市韧性城市海绵城市无废城市建
历史中的包拯身居何职?别再被电视剧骗了,老包就执掌开封府一年提起包拯,很多人的脑海中会浮现出一个额头正中贴着月牙皮肤黝黑的包青天形象,他秉公执法刚正不阿,不畏强权敢于和皇亲国戚作斗争,深受百姓的爱戴。那么历史中的包拯到底是什么样子的呢?网络特斯拉和华为,相差的不仅仅是一个激光雷达华为手机因为芯片供应困难的原因,销量大跌,但是突然在汽车领域的发力,让所有人的吃了一惊,特别是其它的造车企业,华为的加入,让他们感到后背丝丝凉意。华为最近在汽车领域做了三个大动作1被举报,调查,停职,邵淳先生为中国航母走过的曲折历程2012年9月25号,辽宁舰正式交付予中国人民解放军海军,成为中国海军第一艘可以搭载固定翼飞机的航空母舰。结束了我们没有航母的历史,从此,我们的国土防卫力量正式从大陆走向深蓝。20HampampampM事件的幕后黑手,终于找出来了,人称反华妖女许秀中近日随着新疆棉事件的发酵,不少网友也越来越疑惑,整件事情到底是谁在背后推波助澜?HM,耐克阿迪等知名品牌到底有何证据和底气,让他们不惜和整个中国市场硬碰硬?就在近日谜题终于被解开,不吹不黑,试过汉兰达和大众揽境之后,我选择了揽境我和老婆都要上班,所以两个孩子一直是我爸妈和岳父母轮流过来帮忙照顾,有时候,周末想一家去周边地区去旅游一下,因为现在的车太小了,坐不下六个人,每次只能让一个人留在家里看家,所以老婆唐太宗为何改国史?内心有鬼还是好奇心?李世民真的功高盖世?贞观三年,唐太宗下令设置秘书内省专门负责修撰国史,之后又将史馆交由宰相监修,从此史馆就变成皇帝直接控制的一个机构。据说他重置史馆就为了修改国史,那么为何要这么做?历史上的唐太宗到底鬼谷子到底有多神秘?他一生不愿意下山,却为何拥有这么高的声誉鬼谷子是中国历史上最具神秘色彩的人物之一,他隐居山林不问世事,却和孔子孟子庄子荀子等大家齐名,享受世人的赞誉。那么鬼谷子到底是一个什么样的人呢?网络配图他原名叫王诩(xu)又叫王蝉魏忠贤真的是奸臣么?为何杀了魏忠贤,大明朝就垮了?被世人称为奸臣的魏忠贤曾权势滔天,最后害怕被崇祯皇帝清算而上吊自尽。没有想到的是,崇祯走投无路要自杀时,他的心腹王承恩却说了一句话如果魏忠贤没死的话,大明王朝也不会落到如此下场!那徐福帮秦始皇寻找不死药,为何最后消失了?他到底去了哪里?公元前221年,秦始皇统一了六国。当皇帝的感觉虽然累,但真香!如果能永生那就更好了。为了实现这个目的,秦始皇派徐福去仙山寻找不死药,没想到药没找到,最后人也消失了,那么徐福为何消失黑魂解析魂1与魂3里的画中世界究竟藏了多少秘密(上)前言文本大概9000,视频在大概28分钟,可以直接关注我,在我发布的视频里寻找对应视频,也可以直接点击链接去观看视频。黑暗之魂画中世界(上)我们魂3研究的第二期,来讲讲黑魂世界里的黑魂解析黑暗之魂里的黑教会与三姐妹的故事在魂3的世界,所有的一切都开始互相影响,发生了质变与畸形。例如各种楔形石与各种属性交合在了一起。例如有些咒术与奇迹既要求智力又同时要求信仰。例如明明应该照耀与延续生命的火焰开始逐渐