ASM給方法加try catch

期望通過(guò)注解的形式,給方法套上try catch祥山。老樣子淘这,先看一下try catch的字節(jié)碼。

CatchUtil

public class CatchUtil {
    private void handle() {
        try {
            Date date = new Date();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

找到編譯后的class文件撬统,使用插件ASM Bytecode Viewer

 private handle()V
    TRYCATCHBLOCK L0 L1 L2 java/lang/Exception
   L0
    LINENUMBER 8 L0
    NEW java/util/Date
    DUP
    INVOKESPECIAL java/util/Date.<init> ()V
    ASTORE 1
   L1
    LINENUMBER 12 L1
    GOTO L3
   L2
    LINENUMBER 9 L2
   FRAME SAME1 java/lang/Exception
    ASTORE 1
   L4
    LINENUMBER 10 L4
    ALOAD 1
    INVOKEVIRTUAL java/lang/Exception.printStackTrace ()V
   L3
    LINENUMBER 13 L3
   FRAME SAME
    RETURN

重點(diǎn)就是TRYCATCHBLOCK L0 L1 L2 java/lang/Exception适滓。label L0是try塊的起始點(diǎn),label L1是終點(diǎn)恋追。方法正常調(diào)用不出異常的情況下try塊走完GOTO到L3凭迹,return結(jié)束方法;發(fā)生異常到異常處理處L2苦囱,將異常加入局部變量表ASTORE嗅绸,然后ALOAD異常,調(diào)用catch塊中printStackTrace()打印堆棧撕彤。

所以我們需要在方法開(kāi)始時(shí)插入TRYCATCHBLOCK鱼鸠,異常處理標(biāo)簽L2后插入printStackTrace()

先弄個(gè)注解Catch羹铅,只插入注解方法蚀狰。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Catch {
}

CatchMethodVisitor在方法前插入TRYCATCHBLOCK。

object CatchMethodVisitor {

    operator fun invoke(
            mv: MethodVisitor,
            access: Int,
            name: String?,
            descriptor: String?,
    ): MethodVisitor {
        return object : AdviceAdapter(Opcodes.ASM9, mv, access, name, descriptor) {
            private var needTrack = false
            private val start = Label()
            private val end = Label()
            private val catch = Label()

            override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
                if (Type.getDescriptor(Catch::class.java) == descriptor) needTrack = true
                return super.visitAnnotation(descriptor, visible)
            }

            override fun onMethodEnter() {
                if (needTrack) {
                    visitLabel(start)
                    visitTryCatchBlock(start, end, catch, "java/lang/Exception")
                }
                super.onMethodEnter()
            }
        }
    }
}

重寫(xiě)visitMaxs()方法睦裳,在此之前插入catch塊造锅。

            override fun visitMaxs(maxStack: Int, maxLocals: Int) {
                if (needTrack) {
                    mv.visitLabel(end)
                    mv.visitLabel(catch)
                    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, arrayOf<Any>("java/lang/Exception"))
                    val local = newLocal(Type.LONG_TYPE);
                    mv.visitVarInsn(ASTORE, local)
                    mv.visitVarInsn(ALOAD, local)
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false)
                }
                super.visitMaxs(maxStack, maxLocals)
            }

try塊結(jié)束標(biāo)簽end,異常處理標(biāo)簽catch廉邑,然后打印堆棧哥蔚。

看起來(lái)很美好,跑個(gè)測(cè)試用例蛛蒙。

    @Catch
    private fun catchVoid() {
        val date = Date()
    }

    @Catch
    private fun catchObject() = Date()

找到transform中處理過(guò)的class文件糙箍,反編譯成Java。

   @Catch
   private final void catchVoid() {
      try {
         new Date();
      } catch (Exception var3) {
         var3.printStackTrace();
      }
   }

   @Catch
   private final Date catchObject() {
      try {
         return new Date();
      } catch (Exception var2) {
         var2.printStackTrace();
      }
   }

嗯哼~原方法中有返回值就有問(wèn)題了牵祟,插入try catch后方法結(jié)尾沒(méi)有返回值報(bào)錯(cuò)深夯。這里為了方便在catch塊中將異常throw出去,當(dāng)然也可以給方法返回一個(gè)默認(rèn)值诺苹。

            override fun visitMaxs(maxStack: Int, maxLocals: Int) {
                if (needTrack) {
                    mv.visitLabel(end)
                    mv.visitLabel(catch)
                    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, arrayOf<Any>("java/lang/Exception"))
                    val local = newLocal(Type.LONG_TYPE);
                    mv.visitVarInsn(ASTORE, local)
                    mv.visitVarInsn(ALOAD, local)
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false)
                    mv.visitVarInsn(ALOAD, local)
                    mv.visitInsn(ATHROW)
                }
                super.visitMaxs(maxStack, maxLocals)
            }

簡(jiǎn)單的加個(gè)throw拋出異常咕晋,再跑一下測(cè)試用例。

   @Catch
   private final void catchVoid() {
      try {
         new Date();
      } catch (Exception var3) {
         var3.printStackTrace();
         throw var3;
      }
   }

   @Catch
   private final Date catchObject() {
      try {
         return new Date();
      } catch (Exception var2) {
         var2.printStackTrace();
         throw var2;
      }
   }

好起來(lái)了收奔,個(gè)人感覺(jué)在catch塊中處理異常后繼續(xù)throw出去比較好掌呜,畢竟崩潰后定位問(wèn)題也比較容易。返回默認(rèn)值雖然能保證不崩潰坪哄,但業(yè)務(wù)邏輯可能產(chǎn)生其它奇奇怪怪的問(wèn)題质蕉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末势篡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子模暗,更是在濱河造成了極大的恐慌禁悠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兑宇,死亡現(xiàn)場(chǎng)離奇詭異碍侦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)隶糕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)祝钢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人若厚,你說(shuō)我怎么就攤上這事⊙咽玻” “怎么了测秸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)灾常。 經(jīng)常有香客問(wèn)我霎冯,道長(zhǎng),這世上最難降的妖魔是什么钞瀑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任沈撞,我火速辦了婚禮,結(jié)果婚禮上雕什,老公的妹妹穿的比我還像新娘缠俺。我一直安慰自己,他們只是感情好贷岸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布壹士。 她就那樣靜靜地躺著,像睡著了一般偿警。 火紅的嫁衣襯著肌膚如雪躏救。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天螟蒸,我揣著相機(jī)與錄音盒使,去河邊找鬼。 笑死七嫌,一個(gè)胖子當(dāng)著我的面吹牛少办,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抄瑟,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼凡泣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枉疼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鞋拟,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骂维,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后贺纲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體航闺,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年猴誊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潦刃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡懈叹,死狀恐怖乖杠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情澄成,我是刑警寧澤胧洒,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站墨状,受9級(jí)特大地震影響卫漫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肾砂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一列赎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镐确,春花似錦包吝、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至臼氨,卻和暖如春掺喻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背储矩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工感耙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人持隧。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓即硼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親屡拨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子只酥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容