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 這種就只能在一個類實現所有邏輯了