Java探索之——Class字节码与ClassLoader(二)

今天要说的是java字节码的增强,按照惯例,先上代码以后再补说明。

首先是自己定义的一个字节码增强类,采用的是ASM来操纵的。

View Code
 1 /**
 2  * 
 3  * @author rui.chen
 4  *
 5  */
 6 public class ClassAdvice {
 7     /**
 8      * 前增强,在每个方法执行前打印出"Enter Method -> + 方法名"
 9      * @param className 要增强的类名
10      */
11     public void beforeAdvice(String className){
12         try {
13             ClassReader reader = new ClassReader(new FileInputStream(className+".class"));
14             ClassNode cn = new ClassNode();
15             reader.accept(cn, 0);
16             for(Object obj:cn.methods){
17                 MethodNode mn = (MethodNode) obj;
18                 if("<init>".equals(mn.name) || "clinit".equals(mn.name)){
19                     continue;
20                 }
21                 InsnList insns = mn.instructions;
22                 InsnList il = new InsnList();
23                 il.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
24                 il.add(new LdcInsnNode("Enter Method ->"+mn.name));
25                 il.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));
26                 insns.insert(il);
27                 mn.maxStack += 3;
28             }
29             ClassWriter writer = new ClassWriter(0);
30             cn.accept(writer);
31             byte[] bs = writer.toByteArray();
32             FileOutputStream out = new FileOutputStream(className+".class");
33             out.write(bs);
34             out.close();
35         } catch (FileNotFoundException e) {
36             e.printStackTrace();
37         } catch (IOException e) {
38             e.printStackTrace();
39         }
40     }
41     
42 }

然后我们先看一下上一篇中自己编译的类SaySomething的class文件结构,主要关心saySomething方法的字节码。class文件结构的查看可以采用ASM的ASMifierClassVisitor工具,调用其main方法即可打印。

/**
 * SaySomething.java 
 *
 **/
public class SaySomething {
    public void saySomething(String sth){
        System.out.println(sth);
    }
}
View Code
 1 import java.util.*;
 2 import org.objectweb.asm.*;
 3 import org.objectweb.asm.attrs.*;
 4 import org.objectweb.asm.util.ASMifierClassVisitor;
 5 /**
 6  * 使用ASMifierClassVisitor打印结果,命令为ASMifierClassVisitor.main(new String[]{className+".class"});
 7  * @author rui.chen
 8  *
 9  */
10 public class SaySomethingDump implements Opcodes {
11 
12     public static byte[] dump () throws Exception {
13 
14         ClassWriter cw = new ClassWriter(0);
15         FieldVisitor fv;
16         MethodVisitor mv;
17         AnnotationVisitor av0;
18 
19         cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "SaySomething", null, "java/lang/Object", null);
20 
21         {
22             mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
23             mv.visitCode();
24             mv.visitVarInsn(ALOAD, 0);
25             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
26             mv.visitInsn(RETURN);
27             mv.visitMaxs(1, 1);
28             mv.visitEnd();
29         }
30         {
31             mv = cw.visitMethod(ACC_PUBLIC, "saySomething", "(Ljava/lang/String;)V", null, null);
32             mv.visitCode();
33             mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
34             mv.visitVarInsn(ALOAD, 1);
35             mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
36             mv.visitInsn(RETURN);
37             mv.visitMaxs(2, 2);
38             mv.visitEnd();
39         }
40         cw.visitEnd();
41 
42         return cw.toByteArray();
43     }
44 }

然后我们通过一个测试类调用字节码的前增强,看一下字节码发生了哪些变化。

1 ClassAdvice advice = new ClassAdvice();
2 advice.beforeAdvice("SaySomething");
 1 //注意观察多出来的三行
 2         {
 3             mv = cw.visitMethod(ACC_PUBLIC, "saySomething", "(Ljava/lang/String;)V", null, null);
 4             mv.visitCode();
 5             //访问域成员System.out
 6             mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
 7             //将字符串推送至栈顶
 8             mv.visitLdcInsn("Enter Method ->saySomething");
 9             //调用out成员的println方法
10             mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
11             mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
12             mv.visitVarInsn(ALOAD, 1);
13             mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
14             mv.visitInsn(RETURN);
15             mv.visitMaxs(5, 2);
16             mv.visitEnd();
17         }

最后通过反射调用saySomething方法,得到结果如下:

 1 public class AdviceTest {
 2     public static void main(String[] args) throws ClassNotFoundException, 
 3     SecurityException, NoSuchMethodException, IllegalArgumentException, 
 4     IllegalAccessException, InvocationTargetException, InstantiationException{
 5         MyClassLoader loader = new MyClassLoader(".");
 6         Class<?> clazz = loader.loadClass("SaySomething");
 7         Method method = clazz.getMethod("saySomething", new Class[]{String.class});
 8         method.invoke(clazz.newInstance(), "Hello, Rui.Chen!");
 9     }
10 }
1 Enter Method ->saySomething
2 Hello, Rui.Chen!

控制台打印出了"Enter Method ->saySomething",增强成功!

 

转载于:https://www.cnblogs.com/chrhust/archive/2013/05/10/3071549.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值