文章目录

  • ClassWriter的使用
  • 自定义一个ClassVisitor
  • enhancer的使用

ASM是Java中比较流行的用来读写字节码的类库,用来基于字节码层面对代码进行分析和转换。在读写的过程中可以加入自定义的逻辑以增强或修改原来已编译好的字节码,比如CGLIB用它来实现动态代理。ASM被设计用于在运行时对Java类进行生成和转换,当然也包括离线处理。ASM短小精悍、且速度很快,从而避免在运行时动态生成字节码或转换时对程序速度的影响,又因为它体积小巧,可以在很多内存受限的环境中使用。

ClassWriter的使用

ClassVisitor是用来生成asm和改变字节码的,ClassVisitor是一
个访问字节码的框架,其对字节码的创建和修改主要是通过其内部的ClassVisitor具体实现来代理的
ClassWriter:ClassWriter 是ClassVisitor的一个实现类

ClassWriter提供了一类visit方法来编写类。使用visit方法开始编写,visitEnd方法结束编写。

具体的来说,此类visit方法能够为需要修改的类添加属性和方法。例如,visitMethod可以为类添加方法,visitAttribute可以为类添加属性。

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;public static void main(String[] args) throws IOException {ClassWriter cs = new ClassWriter(0);//通过vist确定类的同步信息 java版本号 类修饰符 类的权限定名cs.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"HZYziyi",null, "java/lang/Object",null);//构造函数MethodVisitor mv = cs.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);mv.visitCode();mv.visitVarInsn(Opcodes.ALOAD, 0);mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");mv.visitInsn(Opcodes.RETURN);mv.visitMaxs(1, 1);mv.visitEnd();// 定义code方法MethodVisitor methodVisitor = cs.visitMethod(Opcodes.ACC_PUBLIC, "code", "()V", null, null);methodVisitor.visitCode();methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");methodVisitor.visitLdcInsn("I'm a哈哈哈 Duck,Just Coding.....");methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");methodVisitor.visitInsn(Opcodes.RETURN);methodVisitor.visitMaxs(2, 2);methodVisitor.visitEnd();cs.visitEnd();// 使classWriter类已经完成// 将classWriter转换成字节数组写到文件里面去byte[] data = cs.toByteArray();File file = new File("./HZYziyi.class");FileOutputStream fout = new FileOutputStream(file);fout.write(data);fout.close();}

运行结束和可以看到目录下生成了一个HZYziyi.class文件,可以看到其中的内容:

自定义一个ClassVisitor

写一个类继承ClassVisitor

public class ClassChangeAdapter extends ClassVisitor {public ClassChangeAdapter(ClassVisitor classVisitor) {super(ASM4, classVisitor);}@Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {// 如果当前访问到main方法就移除掉if(name.equals("main")) {return null;}MethodVisitor mv =super.visitMethod(access, name,desc, signature, exceptions);MethodChangeAdaper methodChangeAdaper = new MethodChangeAdaper(ASM4, mv, access, name, desc);return methodChangeAdaper;}}

自定义MethodChangeAdaper类继承AdviceAdapter

public class MethodChangeAdaper extends AdviceAdapter {/*** Creates a new {@link AdviceAdapter}.** @param api    the ASM API version implemented by this visitor. Must be one*               of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.* @param mv     the method visitor to which this adapter delegates calls.* @param access the method's access flags (see {@link Opcodes}).* @param name   the method's name.* @param desc   the method's descriptor (see {@link Type Type}).*/protected MethodChangeAdaper(int api, MethodVisitor mv, int access, String name, String desc) {super(api, mv, access, name, desc);}@Overridepublic void visitCode() {super.visitCode();//调用静态方法mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "before", "()V", false);// 创建新的局部变量表索引// 将int型2推送至操作数栈顶mv.visitInsn(ICONST_2);/// 创建新的局部变量表索引int newLocal =  super.newLocal(jdk.internal.org.objectweb.asm.Type.INT_TYPE);mv.visitIntInsn(ISTORE, newLocal);mv.visitIincInsn(newLocal, 1);mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitIntInsn(ILOAD, newLocal);mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);mv.visitMethodInsn(INVOKESTATIC, "com/tuling/cglib/proxy/DaoAnotherProxy", "after", "()V", false);}
}

在mv.visitMethodInsn(INVOKESTATIC, “com/tuling/cglib/proxy/DaoAnotherProxy”, “before”, “()V”, false);
这句话中调用了自己的包下这个DaoAnotherProxy类的before方法
其中DaoAnotherProxy 类:

其中public class DaoAnotherProxy {public static void before(){System.out.println("前置通知!");}public static void after(){System.out.println("后置通知!");}
}

主类:

public class ClassChangeMethodTest {public static void main(String[] args) throws Exception {ClassWriter cw = new ClassWriter(COMPUTE_MAXS);ClassReader cr = new ClassReader(TestmyQuestion.class.getName());ClassChangeAdapter adapter=new ClassChangeAdapter(cw);cr.accept(adapter,ClassReader.SKIP_FRAMES);byte[] bytes = cw.toByteArray();File file = new File("./TestmyQuestion.class");FileOutputStream fout = new FileOutputStream(file);fout.write(bytes);fout.close();MyClassLoader classLoader=new MyClassLoader();Class loadedClass = classLoader.defineClass(TestmyQuestion.class.getName(), bytes);loadedClass.newInstance();//创建由此Class对象表示的类的新实例。 该类被实例化为一个带有空参数列表的new表达式。 如果尚未初始化该类,则将其初始化}
}

其中TestmyQuestion 类:

public class TestmyQuestion {public static void main(String[] args) {int i=2;i++;System.out.println(i);}
}

上面的程序运行以后可以看到目录下生成了一个class文件TestmyQuestion.class:


import com.tuling.cglib.proxy.DaoAnotherProxy;public class TestmyQuestion {public TestmyQuestion() {DaoAnotherProxy.before();byte var1 = 2;int var2 = var1 + 1;System.out.println(var2);DaoAnotherProxy.after();super();}
}

这即是asm生成的class文件
输出:

enhancer的使用

自定义MethodInterceptor :


/*** 创建一个Dao代理*/
public class DaoProxy implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("begin intercept");//invokeSuper方法调用目标类的方法proxy.invokeSuper(obj, args);System.out.println("end intercept");return obj;}
}

自定义CallbackFilter


import net.sf.cglib.proxy.CallbackFilter;import java.lang.reflect.Method;/*** 返回数值表示顺序*/
public class DaoFilter implements CallbackFilter {@Overridepublic int accept(Method method) {System.out.println("accept");if("select".equalsIgnoreCase(method.getName())) {return 0;} else if ("delete".equalsIgnoreCase(method.getName())) {return 1;}return 2;}
}

接口和实现类:

public interface IDao {public void select();public void insert();}public class Dao implements IDao{@Overridepublic void select() {System.out.println("select 1 from dual:");insert();}@Overridepublic void insert() {System.out.println("insert into ...");}}

主程序:


public class Client {public static void main(String[] args) {//将代理类存到本地磁盘System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");//实例化增强器Enhancer enhancer = new Enhancer();//设置需要代理的目标类enhancer.setSuperclass(Dao.class);//设置拦截对象 回调的实现类enhancer.setCallback(new DaoProxy());//使用create 返回Object 生成代理类并返回实例Dao dao = (Dao) enhancer.create();//select优先级高 使用DaoProxydao.select();//无法代理被final修饰的方法//dao.delete();// dao.insert();}
}

运行主程序可以看到输出:

生成的代理类class文件:


public class Dao implements IDao {public Dao() {}public void select() {DaoAnotherProxy.before();System.out.println("select 1 from dual");}public void insert() {System.out.println("insert into ...");}public final void delete() {System.out.println("delete from ...");}
}

asm中的ClassWriter使用、自定义ClassVisitor和cglib的enhancer使用实例相关推荐

  1. java asm 中文文档_Java ASM3学习(3)

    MethodVisitor ClassVisitor的visitMethod能够访问到类中某个方法的一些入口信息,那么针对具体方法中字节码的访问是由MethodVisitor来进行的 访问顺序如下,其 ...

  2. 如何在sqlite3连接中创建并调用自定义函数

    #!/user/bin/env python # @Time :2018/6/8 14:44 # @Author :PGIDYSQ #@File :CreateFunTest.py '''如何在sql ...

  3. spring mvc项目中利用freemarker生成自定义标签

    2019独角兽企业重金招聘Python工程师标准>>> spring mvc项目中利用freemarker生成自定义标签 博客分类: java spring mvc +freemar ...

  4. object类中的equals与自定义equals方法详解

    object类中的equals与自定义equal方法详解 1.this怎么理解?this == obj表示什么? this就是当前你new出来的对象,这里指谁调用equal方法this指的就是谁,ob ...

  5. c++定义一个动态对象数组_如何在Python中自定义一个可被调用的对象实例?

    前言 在关于Python描述符函数的详解三篇中,我们有提到如何基于类创建一个"描述符函数",之所以能够基于类创建这样一个概念,是因为用到了类中的__call__属性,从前述文章中可 ...

  6. java 自定义arraylist_Java 中模仿源码自定义ArrayList

    Java 中模仿源码自定义ArrayList 最近看了下ArrayList的源码,抽空根据ArrayList的底层结构写了一个功能简单无泛型的自定义ArrayLsit,帮助自己更好理解ArrayLis ...

  7. Excel 2007中创建或删除自定义数字格式

    Excel 2007提供了许多内置数字格式,但如果这些格式无法满足您的需要,您可以自定义内置数字格式以便创建自己的数字格式. 创建自定义数字格式 1.打开要创建并存储自定义数字格式的工作簿. 2.在& ...

  8. 五十八、深入了解 Java 中的注解和自定义注解

    @Author:Runsen @Date:2020/7/9 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  9. 笔记三 vue中封装复用 过滤器 自定义组件 vue中component选项

    题外话: npm install -g express-generator npm安装express框架 封装复用 Vue中关于封装复用的内容,属于Vue中的进阶知识,在实战中对开发者的抽象和泛化能力 ...

最新文章

  1. 剑指offer:面试题24. 反转链表
  2. Java API —— Collections类
  3. php 检查图片重复度,php – 检测图片的“整体平均”颜色
  4. poj 3680 Intervals(离散化+费用流)
  5. java计算时间差_JAVA并发编程三大Bug源头(可见性、原子性、有序性),彻底弄懂...
  6. android activity 被notification启动,Android通知Notification全面剖析
  7. [原创]flex 3 + .net开发flash Remoting一 --- 开发环境
  8. c/c++ code JSON
  9. ACKRec:注意力异构图卷积深度知识推荐器 SIGIR 2020
  10. linux任务计划cron
  11. LINUX异步信号集合示例代码
  12. sap不用oracle数据库库,SAP系统安装之Oracle 10g数据库(Win3264)
  13. 机器学习(2)——监督学习
  14. 入门短视频剪辑,可以选择什么类型的做?
  15. python提取图片中的文字自动填表,python提取图片中的文字并生成word文档
  16. 正宇丨有钱,把日子过好;没钱,把心情过好
  17. MATLAB入门实战版
  18. [转]马化腾:如何从“较好”到“最好”
  19. 老虎证券社招java工程师
  20. PAT A1155 Heap Paths ——三更灯火五更鸡?

热门文章

  1. python股票分析-放量跌到底买入-涨了再卖
  2. tar包解压和打包方法
  3. 短路与和与、短路或和或的区别
  4. Angular4 幕课网
  5. matlab的给平面图,matlab画相平面图
  6. MySQL数据库引擎、数据事务与隔离级别
  7. 数字逻辑代数基础——基本定理、公式与卡诺图化简法、格雷码、LSFR等
  8. 萨满祭司专业技能100%全分析
  9. 计算机软考落户广州,想要在2020年入户广州,考这个证就够了!
  10. Spring Framework与JDK版本对应表