1. 预期目标
使用ASM为方法添加try-catch代码块 比如有一个Hello类
package org. example. asm8. trycatch ;
public class Hello {
public void compute ( String name, int age) {
int length = name. length ( ) ;
System . out. println ( name + " length = " + length) ;
int div = div ( age) ;
System . out. println ( "996 / " + age + " = " + div) ;
}
private int div ( int age) {
return 996 / age;
}
}
package org. example. asm8. trycatch ;
public class Hello {
public void compute ( String name, int age) {
try {
int length = name. length ( ) ;
System . out. println ( name + " length = " + length) ;
int div = this . div ( age) ;
System . out. println ( "996 / " + age + " = " + div) ;
} catch ( Exception var6) {
PrintUtils . printException ( "compute" , var6) ;
throw var6;
}
}
private int div ( int age) {
try {
return 996 / age;
} catch ( Exception var3) {
PrintUtils . printException ( "div" , var3) ;
throw var3;
}
}
}
2. 代码实现
2.1 打印异常类
package org. example. asm8. trycatch ;
public class PrintUtils {
public static void printException ( String methodName, Exception exception) {
System . out. println ( "监控 -> [方法名:" + methodName + ",异常信息:" + exception. getMessage ( ) + "]" ) ;
}
}
2.2 字节码增强类
package org. example. asm8. trycatch ;
import java. io. FileOutputStream ;
import java. io. IOException ;
import org. objectweb. asm. ClassReader ;
import org. objectweb. asm. ClassVisitor ;
import org. objectweb. asm. ClassWriter ;
import org. objectweb. asm. Label ;
import org. objectweb. asm. MethodVisitor ;
import org. objectweb. asm. Opcodes ;
import org. objectweb. asm. Type ;
import org. objectweb. asm. commons. AdviceAdapter ;
public class AddTryCatch implements Opcodes {
public static void main ( String [ ] args) throws IOException {
ClassReader cr = new ClassReader ( Hello . class . getName ( ) ) ;
ClassWriter cw = new ClassWriter ( cr, ClassWriter . COMPUTE_FRAMES) ;
cr. accept ( new MyClassVisitor ( ASM9, cw) , ClassReader . EXPAND_FRAMES) ;
byte [ ] bytes = cw. toByteArray ( ) ;
String path = AddTryCatch . class . getResource ( "/" ) . getPath ( ) + "org/example/asm8/trycatch/Hello.class" ;
System . out. println ( "输出路径:" + path) ;
try ( FileOutputStream fos = new FileOutputStream ( path) ) {
fos. write ( bytes) ;
}
}
static class MyClassVisitor extends ClassVisitor {
protected MyClassVisitor ( int api, ClassVisitor classVisitor) {
super ( api, classVisitor) ;
}
@Override
public MethodVisitor visitMethod ( int access, String name, String descriptor, String signature,
String [ ] exceptions) {
MethodVisitor methodVisitor = super . visitMethod ( access, name, descriptor, signature, exceptions) ;
if ( methodVisitor != null ) {
boolean isConstructor = "<init>" . equals ( name) ;
boolean isAbstractMethod = ( access & ACC_ABSTRACT) != 0 ;
boolean isNativeMethod = ( access & ACC_NATIVE) != 0 ;
if ( ! isConstructor && ! isAbstractMethod && ! isNativeMethod) {
methodVisitor = new MyMethodVisitor ( api, methodVisitor, access, name, descriptor) ;
}
}
return methodVisitor;
}
}
static class MyMethodVisitor extends AdviceAdapter {
int localIndex;
private final Label startLabel = new Label ( ) ;
private final Label endLabel = new Label ( ) ;
private final Label handlerLabel = new Label ( ) ;
protected MyMethodVisitor ( int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
super ( api, methodVisitor, access, name, descriptor) ;
}
@Override
protected void onMethodEnter ( ) {
super . visitLabel ( startLabel) ;
super . onMethodEnter ( ) ;
}
@Override
public void visitMaxs ( int maxStack, int maxLocals) {
super . visitLabel ( endLabel) ;
super . visitLabel ( handlerLabel) ;
localIndex = nextLocal;
super . visitVarInsn ( ASTORE, localIndex) ;
printException ( ) ;
super . visitVarInsn ( ALOAD, localIndex) ;
super . visitInsn ( ATHROW) ;
super . visitTryCatchBlock ( startLabel, endLabel, handlerLabel, "java/lang/Exception" ) ;
super . visitMaxs ( maxStack, maxLocals) ;
}
private void printException ( ) {
super . visitLdcInsn ( getName ( ) ) ;
super . visitVarInsn ( ALOAD, localIndex) ;
super . visitMethodInsn ( INVOKESTATIC, Type . getInternalName ( PrintUtils . class ) , "printException" ,
"(Ljava/lang/String;Ljava/lang/Exception;)V" , false ) ;
}
}
}
2.3 执行增强
执行main方法,查看被修改后的Hello.class,可以看出已经在方法中添加了try-catch和exception的信息打印
2.4 验证
package org. example. asm8. trycatch ;
public class HelloRun {
public static void main ( String [ ] args) {
Hello hello = new Hello ( ) ;
hello. compute ( "Fisher" , 0 ) ;
}
}
Fisher length = 6
监控 -> [ 方法名:div,异常信息:/ by zero]
监控 -> [ 方法名:compute,异常信息:/ by zero]
Exception in thread "main" java. lang. ArithmeticException: / by zero
at org. example. asm8. trycatch. Hello. div ( Hello . java: 13 )
at org. example. asm8. trycatch. Hello. compute ( Hello . java: 8 )
at org. example. asm8. trycatch. HelloRun. main ( HelloRun . java: 7 )