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 这种就只能在一个类实现所有逻辑了

posted @ 2020-07-09 14:15  java拌饭  阅读(1063)  评论(0编辑  收藏  举报