曹工说Spring Boot源码(24)-- Spring注解扫描的瑞士军刀,asm技术实战(上) (3)

所以,ClassWriter,大家一定要好好理解,这个ClassWriter,主要的使用方法就是:提供给你一堆方法,你可以调用他们,来给里面的field设置东西,比如,你要设置类名,那你就调用:

cw.visit(V1_7, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "pkg/Comparable", null, "java/lang/Object", null);

要加个field,那就这样:

cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, new Integer(0)).visitEnd(); ClassWriter为啥要实现ClassVisitor

如小标题所言,ClassWriter是实现了ClassVisitor的。

public class ClassWriter extends ClassVisitor

前面我们说的那些,手动去调用的方法,也是来源于ClassVisitor的。

cw.visit(V1_7, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "pkg/Comparable", null, "java/lang/Object", null);

该方法,来源于:

org.objectweb.asm.ClassVisitor#visit public void visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { if (cv != null) { cv.visit(version, access, name, signature, superName, interfaces); } }

那么,接下来这段话,大家好好理解下:

前面的demo中,我们手动调用了ClassWriter的各种visit方法,去生成class;但是,我们又知道,ClassWriter的那些方法,来自于ClassVisitor,而:当我们向下面这样来编码的时候,ClassVisitor的方法会自动被调用(忘了的,往前翻到:ASM的核心之读取功能),那么,我们可以实现如下的class复制功能了:

package com.yn.classgenerate; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import static org.objectweb.asm.Opcodes.ASM4; public class CopyClassVersion1 { public static void main(String[] args) throws IOException { ClassReader classReader = new ClassReader("com.yn.classgenerate.CopyClass"); //1 ClassWriter cw = new ClassWriter(0); //2 classReader.accept(cw, 0); byte[] b2 = cw.toByteArray(); File file = new File("F:\\gitee-ckl\\all-simple-demo-in-work\\asm-demo\\src\\main\\java\\com\\yn\\classgenerate\\CopyClass2.class"); FileOutputStream fos = new FileOutputStream(file); fos.write(b2); fos.close(); } }

这里的核心,就是要把classWriter,当成ClassVisitor,传递给ClassReader。

上述代码点1,此时,classWriter内部是空的,没法生成一个class

传递给classReader后,随着classReader不断去解析com.yn.classgenerate.CopyClass这个类,classWriter的各个visit方法,不断被回调,因此,com.yn.classgenerate.CopyClass的各类field、method等,不断被写入classWriter中,于是,复制就这样完成了。

ClassVisitor那些链式操作

前面那个复制class的操作中,classreader是直接回调classWriter的,我们其实也可以在中间横插一脚。

public class CopyClass { public static void main(String[] args) throws IOException { ClassReader classReader = new ClassReader("com.yn.classgenerate.CopyClass"); ClassWriter cw = new ClassWriter(0); // cv forwards all events to cw ClassVisitor cv = new ClassVisitor(ASM4, cw) { }; classReader.accept(cv, 0); byte[] b2 = cw.toByteArray(); File file = new File("F:\\gitee-ckl\\all-simple-demo-in-work\\asm-demo\\src\\main\\java\\com\\yn\\classgenerate\\CopyClass2.class"); FileOutputStream fos = new FileOutputStream(file); fos.write(b2); fos.close(); } }

在上面这个例子中,我们从classReader的下面这句开始看:

classReader.accept(cv, 0);

那么,可以知道,classReader是去回调cv,那么cv是谁?

ClassVisitor cv = new ClassVisitor(ASM4, cw) { };

cv的构造函数里,传入了cw,cw呢,就是classwriter。

现在的链路是这样的:

classReader --> cv --> cw。

上面这个链路中,classReader肯定会回调cv,但是cv,怎么就确定它会当个二传手呢?

看看ClassVisitor的构造函数:

public ClassVisitor(final int api, final ClassVisitor classVisitor) { if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { throw new IllegalArgumentException(); } this.api = api; this.cv = classVisitor; }

其把ClassVisitor保存到了一个域:cv中。这个cv如何被使用呢?我们看看下面的方法:

org.objectweb.asm.ClassVisitor#visit public void visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { if (cv != null) { cv.visit(version, access, name, signature, superName, interfaces); } } public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { if (cv != null) { return cv.visitAnnotation(descriptor, visible); } return null; }

这就有意思了,如果cv不为null,就调用cv去处理,这就是个delegate啊,代理啊。

中间商搞鬼那些事

上面的demo中,cv简直是尽忠职守,自己在中间,丝毫不做什么事,就是一个称职的代理。但不是所有代理都需要这样,甚至是不鼓励这样。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfxsf.html