ASM之ClassVisitor类设计
ClassVisitor
- 访问者模式
- 我们不讲访问者模式,只说说这个类的设计的个人思考
package org.springframework.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in
* the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
* <tt>visitModule</tt> ][ <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
* <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* (
* <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )*
* <tt>visitEnd</tt>.
*
* @author Eric Bruneton
*/
public abstract class ClassVisitor {
protected final int api;
protected ClassVisitor cv;
public ClassVisitor(final int api) {
this(api, null);
}
public ClassVisitor(final int api, final ClassVisitor cv) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
this.cv = cv;
}
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
if (cv != null) {
cv.visit(version, access, name, signature, superName, interfaces);
}
}
public void visitSource(String source, String debug) {
if (cv != null) {
cv.visitSource(source, debug);
}
}
public ModuleVisitor visitModule(String name, int access, String version) {
if (api < Opcodes.ASM6) {
throw new RuntimeException();
}
if (cv != null) {
return cv.visitModule(name, access, version);
}
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
if (cv != null) {
cv.visitOuterClass(owner, name, desc);
}
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (cv != null) {
return cv.visitAnnotation(desc, visible);
}
return null;
}
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
/* SPRING PATCH: REMOVED FOR COMPATIBILITY WITH CGLIB 3.1
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
*/
if (cv != null) {
return cv.visitTypeAnnotation(typeRef, typePath, desc, visible);
}
return null;
}
public void visitAttribute(Attribute attr) {
if (cv != null) {
cv.visitAttribute(attr);
}
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
if (cv != null) {
cv.visitInnerClass(name, outerName, innerName, access);
}
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
if (cv != null) {
return cv.visitField(access, name, desc, signature, value);
}
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if (cv != null) {
return cv.visitMethod(access, name, desc, signature, exceptions);
}
return null;
}
public void visitEnd() {
if (cv != null) {
cv.visitEnd();
}
}
}
为什么不声明为接口?
- 构造时需要一个成员变量,接口不支持。
为什么要使用静态代理?
- 像下面这样声明不香吗?
public abstract class ClassVisitorV2 {
protected final int api;
public ClassVisitorV2(final int api) {
if (api < Opcodes.ASM4 || api > Opcodes.ASM6) {
throw new IllegalArgumentException();
}
this.api = api;
}
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
}
public void visitSource(String source, String debug) {
}
public ModuleVisitor visitModule(String name, int access, String version) {
if (api < Opcodes.ASM6) {
throw new RuntimeException();
}
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public AnnotationVisitor visitTypeAnnotation(int typeRef,
TypePath typePath, String desc, boolean visible) {
/* SPRING PATCH: REMOVED FOR COMPATIBILITY WITH CGLIB 3.1
if (api < Opcodes.ASM5) {
throw new RuntimeException();
}
*/
return null;
}
public void visitAttribute(Attribute attr) {
}
public void visitInnerClass(String name, String outerName,
String innerName, int access) {
}
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value) {
return null;
}
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
return null;
}
public void visitEnd() {
}
}
-
使用者只需要实现自己需要的方法,类似缺省适配器模式的声明
-
那为什么实际源码里要使用静态代理模式,声明一个ClassVisitor成员变量?
- 个人猜测问题还是出在api成员变量上,我们看到原本有两个方法里面用到了这个变量,这里面被注释了一个,但是没准后面更多的方法也要用到这个变量,如
public ModuleVisitor visitModule(String name, int access, String version) { if (api < Opcodes.ASM6) { throw new RuntimeException(); } if (cv != null) { return cv.visitModule(name, access, version); } return null; }
- 如果使用我们说的这种声明方式,那么子类只需要重写父类方法,如果没有调用super.visitModule方法,这里面的判断逻辑就会丢掉了,也就是api判断没了。
classReader.accept(new ClassVisitorV2(Opcodes.ASM6) { @Override public ModuleVisitor visitModule(String name, int access, String version) { return null; } }, 0);
- 如果按照下面使用,应该也是没啥问题的,api的判断也有了,有个问题就是没办法强制使用者去调用父类visitModule方法,用户也没法意识到这个问题。
classReader.accept(new ClassVisitorV2(Opcodes.ASM6) { @Override public ModuleVisitor visitModule(String name, int access, String version) { super.visitModule(name, access, version); return null; } }, 0);
- 当然,源码里的这种声明方式,如果我们使用覆盖的方式来实现逻辑,如果没有调用super.visitModule方法,跟我说的声明方式也就没区别了,这样也会丢失api的判断
classReader.accept(new ClassVisitor(Opcodes.ASM6) { @Override public ModuleVisitor visitModule(String name, int access, String version) { return null; } }, 0);
- 当然,也可以在子类调用super.visitModule,这样api的判断还是有的
classReader.accept(new ClassVisitor(Opcodes.ASM6) { @Override public ModuleVisitor visitModule(String name, int access, String version) { super.visitModule(name, access, version); return null; } }, 0);
-
上面说的,都得依赖用户知道每个方法需不需要调用父类的被重写方法,这个依赖是不明确且有风险的
-
说了这么多,源码里这么设计的好处是什么呢?可以使用类似装饰模式的分离职责包装我们的具体实现类,ClassVisitor负责装饰;我们的实现类负责重写逻辑,不用关心父类调用。
-
所以源码里既然用了这种设计方式,应该是希望我们这样用吧
classReader.accept(new ClassVisitor(Opcodes.ASM6,new MyClassVisitor(Opcodes.ASM6)) { }, 0); class MyClassVisitor extends ClassVisitor { public MyClassVisitor(int api) { super(api); } @Override public ModuleVisitor visitModule(String name, int access, String version) { return null; } }
- 这样我们的MyClassVisitor就可以随便重写方法,而不用担心一些必要判断丢失了
-
其他的Visitor也采用了类似的设计
-
另外,asm的官方文档提到,ClassVisitor可以当做过滤器来使用,也就是多个ClassVisitor互相嵌套,每个ClassVisitor实现不同职责;ClassVisitorV2 这种就只能在一个类实现所有逻辑了