<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          try-with-resources 中的一個坑,注意避讓

          共 5621字,需瀏覽 12分鐘

           ·

          2022-06-25 11:14

          小伙伴們好呀,昨天復(fù)盤以前做的項(xiàng)目(大概有一年了),看到這個  try-catch ,又想起自己之前掉坑的這個經(jīng)歷 ,弄了個小 demo 給大家感受下~  ??

          問題1

          一個簡單的下載文件的例子。

          這里會出現(xiàn)什么情況呢?

           @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;charset=UTF-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);
                  }

              }

          看完后你覺得選啥呢?

          1. 異常被全局異常處理器捕獲并返回給前端。
          2. 前端收不到 response 的錯誤信息。

          答案當(dāng)然是 2 啦,哈哈 正常的話就不會寫出來了 ??

          bug 回憶

          當(dāng)時和前端聯(lián)調(diào)時,我發(fā)現(xiàn)這個異常信息前端都沒有給出相應(yīng)的提示,還以為是前端的問題,哈哈哈 畢竟我這代碼看著也沒毛病呀。??

          而且項(xiàng)目是前后端分離的,response 的 content-type 和 header 中都做了處理,前端用了 axios 去攔截這些響應(yīng),貌似還有一個 responseType: blob 這樣的東東。然后剛好那會前端也不熟悉這個東西,他也以為是他前端出了問題,但是debug 的時候,看到這個 post 請求的 response 怎么是空的呢,通過 chrome 瀏覽器發(fā)現(xiàn)的。

          這個時候我還很納悶,問他說,難道你這個 前端攔截 處理掉了,不然怎么看不到??(我真坑??,現(xiàn)在真想給自己兩巴掌醒醒?? 這盡說胡話??)

          后來我也覺得不對勁,就仔細(xì)去看自己的代碼了,還叫了另一個同事一起看 ??  一起猜測(中途又坑了前端一把 罪過啊……??)

          一兩個鐘過去后,我終于開竅了,想到會不會是這個 流先被關(guān)閉了 ,才導(dǎo)致這場鬧劇的?? (心里估摸著 八九不離十)

          于是我便嘗試性地修改下代碼,拆開 try-with-resources ,改成常規(guī)的  try-catch ,并在 finally 中重寫了這個流的關(guān)閉邏輯,當(dāng)程序正常時,才正常關(guān)閉流,否則不關(guān)閉。

          結(jié)果很順利地就解決了這個問題…… ??

          當(dāng)時也是覺得自己特蠢,第一時間居然沒想到這個流被關(guān)閉的問題,還傻乎乎地懷疑這個瀏覽器,前端的一些寫法是不是有問題,很尷尬?? 這么坑,,只想趕緊找個洞鉆進(jìn)去。。

          再次看到這個代碼,覺得里面應(yīng)該還有東西可以細(xì)挖出來的,于是便有了這文~ ??(公開處刑,引以為戒)

          問題2

          你有看過  try-with-resources  和  try-catch 編譯后和反編譯出來的代碼嗎? 有對比過他們的不同嗎~

          整體
          細(xì)節(jié)

          這里給出了上面  try-with-resources  模塊反編譯后的代碼,可以發(fā)現(xiàn)反編譯后代碼中是沒有出現(xiàn) finally 塊的。

          如果從上圖看的話, try-with-resources  的作用就是下面兩點(diǎn)了

          1. catch Exception 時,先關(guān)閉流,再拋出異常
          2. 添加正常關(guān)閉流的代碼

          細(xì)心的小伙伴是不是還發(fā)現(xiàn)了這一行代碼呢 ??

          var15.addSuppressed(var12);

          這樣就挖到  Throwable 來了??

          image-20220413230827492

          這個方法的作用請看 ??

          鏈接:https://blog.csdn.net/qiyan2012/article/details/116173807

          大概意思就是把異常掛到最外層的異常中去 ?? ,不過從方法的注釋上可以知道,這個一般都是  try-with-resources  偷偷幫我們做的。

          到這里還不能結(jié)束 ,請接著看 ??

          問題3

          這個異常還沒 debug 呢,別走呀,驗(yàn)證一下上面 流的關(guān)閉 邏輯??

          在 OutputStream的 close 方法中打個斷點(diǎn),最后會來到 Tomcat 的 CoyoteOutputStream 中,可以看到此時的標(biāo)志位 closed 和 doFlush 都是 false。

          執(zhí)行完 close 方法關(guān)閉后,這個 initial 從 true 變?yōu)?false ,而 closed 也變?yōu)?true。

          同時,這個 堆內(nèi)內(nèi)存緩沖區(qū) HeapByteBuffer 中還沒來得及寫入新的數(shù)據(jù),就直接被關(guān)閉了,里面的內(nèi)容還是我上一次訪問留下的。??

          關(guān)閉流后,才去捕獲這個異常,這和我們反編譯后看到的代碼邏輯是一致的

          下面步驟有點(diǎn)長,就簡單概括下關(guān)鍵點(diǎn)~ ??

          流關(guān)閉后,這部分代碼還是照常執(zhí)行的。

          1. 拋出的異常被 SpringMVC 框架的 AbstractHandlerMethodExceptionResolver 捕獲,并執(zhí)行 doResolveHandlerMethodException 去處理
          2. 利用 jackson 的 UTF8JsonGenerator 去進(jìn)行序列化,并用 NonClosingOutputStream  對 OutputStream 進(jìn)行包裝。
          3. 數(shù)據(jù)寫入緩沖區(qū) (關(guān)鍵步驟 如下圖??)

          可以看到流關(guān)閉后,這里 closed 也變成 true,所以自定義的信息也寫不到這個緩沖區(qū)。

          后面的其他 flush 操作也刷不出任何東西了。

          例子的話就放到 GitHub 上了……  直接和下期要寫的例子一起放上去了??

          https://github.com/Java4ye/springboot-demo-4ye

          總結(jié)

          看完之后,你知道了我曾經(jīng)犯過的一個很低級的錯誤?? (這次臉都不要了,硬是挖了點(diǎn)其他內(nèi)容一起寫出來 ??)

          1. 注意流關(guān)閉的問題
          2. 謹(jǐn)慎使用  try-with-resources ,要考慮出異常時,這個流可不可以關(guān)閉。
          3. 同時也知道了  try-with-resources 的一些技術(shù)細(xì)節(jié),不會生成 finally 模塊(我之前的誤區(qū)??),而是會在異常捕獲中幫我們關(guān)閉流,同時附加關(guān)閉過程的異常到最外層的異常,而且在程序的結(jié)尾增加關(guān)閉流的代碼。
          4. 流關(guān)閉后,數(shù)據(jù)再也寫不到緩沖區(qū)中,同時 nio 的 堆內(nèi)內(nèi)存緩存區(qū) HeapByteBuffer 中的數(shù)據(jù)仍然是舊的。后面不管怎么 flush 都無法給到有效反饋信息給前端。

          往期推薦

          33歲程序員的年中總結(jié)


          面渣逆襲:MySQL六十六問!建議收藏


          實(shí)戰(zhàn):10 種實(shí)現(xiàn)延遲任務(wù)的方法,附代碼!


          瀏覽 44
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久视频免费观看 | 看A片视频 | 99色在线视频 | 二级黄色电影免费看 | 黄色电影在线播放网址 |