tp5手机网站开发,网络营销策略分析案例,服务平台的宗旨,什么叫vi形象设计小伙伴们好呀#xff0c;昨天复盘以前做的项目#xff08;大概有一年了#xff09;#xff0c;看到这个 try-catch #xff0c;又想起自己之前掉坑的这个经历 #xff0c;弄了个小 demo 给大家感受下~ #x1f604;问题1一个简单的下载文件的例子。这里会出现什么情况… 小伙伴们好呀昨天复盘以前做的项目大概有一年了看到这个 try-catch 又想起自己之前掉坑的这个经历 弄了个小 demo 给大家感受下~ 问题1一个简单的下载文件的例子。这里会出现什么情况呢GetMapping(/download)public void downloadFile(HttpServletResponse response) throws Exception {String resourcePath /java4ye.txt;URL resource DemoApplication.class.getResource(resourcePath);String path resource.getPath().replace(%20, );try( ServletOutputStream outputStream response.getOutputStream();FileInputStream fileInputStream new FileInputStream(path)) {byte[] bytes new byte[8192];ByteArrayOutputStream baos new ByteArrayOutputStream();int len 0;while ((len fileInputStream.read(bytes)) ! -1) {baos.write(bytes, 0, len);}String fileName java4ye.txt;// response.setHeader(content-type, application/octet-stream;charsetUTF-8);
// response.setContentType(application/octet-stream);
// response.setHeader(Access-Control-Expose-Headers, File-Name);
// response.setHeader(File-Name, fileName);// 异常int i 1/0;response.setHeader(Content-Disposition, attachment;filename fileName);outputStream.write(baos.toByteArray());} catch (Exception e) {throw new DownloadException(e);}}看完后你觉得选啥呢异常被全局异常处理器捕获并返回给前端。前端收不到 response 的错误信息。答案当然是 2 啦哈哈 正常的话就不会写出来了 bug 回忆当时和前端联调时我发现这个异常信息前端都没有给出相应的提示还以为是前端的问题哈哈哈 毕竟我这代码看着也没毛病呀。而且项目是前后端分离的response 的 content-type 和 header 中都做了处理前端用了 axios 去拦截这些响应貌似还有一个 responseType: blob 这样的东东。然后刚好那会前端也不熟悉这个东西他也以为是他前端出了问题但是debug 的时候看到这个 post 请求的 response 怎么是空的呢通过 chrome 浏览器发现的。这个时候我还很纳闷问他说难道你这个 前端拦截 处理掉了不然怎么看不到我真坑现在真想给自己两巴掌醒醒 这尽说胡话后来我也觉得不对劲就仔细去看自己的代码了还叫了另一个同事一起看 一起猜测中途又坑了前端一把 罪过啊……一两个钟过去后我终于开窍了想到会不会是这个 流先被关闭了 才导致这场闹剧的 心里估摸着 八九不离十于是我便尝试性地修改下代码拆开 try-with-resources 改成常规的 try-catch 并在 finally 中重写了这个流的关闭逻辑当程序正常时才正常关闭流否则不关闭。结果很顺利地就解决了这个问题…… 当时也是觉得自己特蠢第一时间居然没想到这个流被关闭的问题还傻乎乎地怀疑这个浏览器前端的一些写法是不是有问题很尴尬 这么坑只想赶紧找个洞钻进去。。再次看到这个代码觉得里面应该还有东西可以细挖出来的于是便有了这文~ 公开处刑引以为戒问题2你有看过 try-with-resources 和 try-catch 编译后和反编译出来的代码吗? 有对比过他们的不同吗~整体细节这里给出了上面 try-with-resources 模块反编译后的代码可以发现反编译后代码中是没有出现 finally 块的。如果从上图看的话 try-with-resources 的作用就是下面两点了catch Exception 时先关闭流再抛出异常添加正常关闭流的代码细心的小伙伴是不是还发现了这一行代码呢 var15.addSuppressed(var12);这样就挖到 Throwable 来了image-20220413230827492这个方法的作用请看 链接https://blog.csdn.net/qiyan2012/article/details/116173807大概意思就是把异常挂到最外层的异常中去 不过从方法的注释上可以知道这个一般都是 try-with-resources 偷偷帮我们做的。到这里还不能结束 ,请接着看 问题3这个异常还没 debug 呢别走呀验证一下上面 流的关闭 逻辑在 OutputStream的 close 方法中打个断点最后会来到 Tomcat 的 CoyoteOutputStream 中可以看到此时的标志位 closed 和 doFlush 都是 false。执行完 close 方法关闭后这个 initial 从 true 变为 false 而 closed 也变为 true。同时这个 堆内内存缓冲区 HeapByteBuffer 中还没来得及写入新的数据就直接被关闭了里面的内容还是我上一次访问留下的。关闭流后才去捕获这个异常这和我们反编译后看到的代码逻辑是一致的下面步骤有点长就简单概括下关键点~ 流关闭后这部分代码还是照常执行的。抛出的异常被 SpringMVC 框架的 AbstractHandlerMethodExceptionResolver 捕获并执行 doResolveHandlerMethodException 去处理利用 jackson 的 UTF8JsonGenerator 去进行序列化并用 NonClosingOutputStream 对 OutputStream 进行包装。数据写入缓冲区 关键步骤 如下图可以看到流关闭后这里 closed 也变成 true所以自定义的信息也写不到这个缓冲区。后面的其他 flush 操作也刷不出任何东西了。例子的话就放到 GitHub 上了…… 直接和下期要写的例子一起放上去了https://github.com/Java4ye/springboot-demo-4ye总结看完之后你知道了我曾经犯过的一个很低级的错误 这次脸都不要了硬是挖了点其他内容一起写出来 注意流关闭的问题谨慎使用 try-with-resources 要考虑出异常时这个流可不可以关闭。同时也知道了 try-with-resources 的一些技术细节不会生成 finally 模块我之前的误区而是会在异常捕获中帮我们关闭流同时附加关闭过程的异常到最外层的异常而且在程序的结尾增加关闭流的代码。流关闭后数据再也写不到缓冲区中同时 nio 的 堆内内存缓存区 HeapByteBuffer 中的数据仍然是旧的。后面不管怎么 flush 都无法给到有效反馈信息给前端。往期推荐33岁程序员的年中总结面渣逆袭MySQL六十六问建议收藏实战10 种实现延迟任务的方法附代码