L'utilisation du modèle de visiteur dans le cadre ASM

La définition du modèle de visiteur consiste à encapsuler certaines opérations qui agissent sur chaque élément d'une certaine structure de données. Il peut définir de nouvelles opérations qui agissent sur ces éléments sans modifier la structure des données.

ASMLe framework utilise le modèle de visiteur pour encapsuler les classopérations de divers éléments de la structure du fichier. Nous ASMapprendrons ASMl'application du modèle de visiteur dans le framework en implémentant une version simple du framework .

Définissez d'abord l'interface de visiteur de classe ClassVisitor, le code est le suivant.

public interface ClassVisitor {
    // 设置class文件结构的版本号、类的访问标志、类名
    void visit(int version, String access, String className);
    // 为类添加一个字段
    FieldVisitor visitField(String access, String name, String descriptor);
    // 为类添加一个方法
    MethodVisitor visitMethod(String access, String name, String descriptor);
}
复制代码

Nous définissons trois méthodes pour les visiteurs de la classe. La visitméthode peut définir classle numéro de version de la structure du fichier, l'indicateur d'accès à la classe et le nom de la classe, la visitFieldméthode peut ajouter un champ visitMethodà la classe et la méthode peut ajouter une méthode à la classe.

Étant donné que l'élément de champ est également une structure de données, le modèle de visiteur peut également être utilisé pour encapsuler les opérations de chaque élément dans la structure de champ. Par exemple, vous visitAnnotationpouvez ajouter un commentaire au champ en appelant la méthode du visiteur du champ . FieldVisitorLa définition de l' interface visiteur sur le terrain est la suivante.

public interface FieldVisitor {
    // 为字段添加一个注解
    void visitAnnotation(String annotation, boolean runtime);
}
复制代码

La classe d'implémentation de l'interface visiteur sur le terrain FieldWriter, le code est le suivant.

@Getter
public class FieldWriter implements FieldVisitor {

    private String access;
    private String name;
    private String descriptor;
    private List<String> annotations;

    public FieldWriter(String access, String name, String descriptor) {
        this.access = access;
        this.name = name;
        this.descriptor = descriptor;
        this.annotations = new ArrayList<>();
    }

    @Override
    public void visitAnnotation(String annotation, boolean runtime) {
        this.annotations.add("注解:" + annotation + ",是否" + runtime);
    }

}
复制代码

Comme la structure de champ, la structure de méthode peut également utiliser le modèle de visiteur pour encapsuler le fonctionnement de chaque élément. Par exemple, en appelant la visitMaxspile d'opérandes de méthode définissable du visiteur de la méthode et la taille de la table de variables locale. MethodVisitorLa définition de l' interface visiteur de la méthode est la suivante.

public interface MethodVisitor {
    // 设置局部变量表和操作数栈的大小
    void visitMaxs(int maxStackSize, int maxLocalSize);
}
复制代码

La classe d'implémentation de l'interface visiteur de la méthode MethodWriter, le code est le suivant.

@Getter
public class MethodWriter implements MethodVisitor {

    private String access;
    private String name;
    private String descriptor;
    private int maxStackSize;
    private int maxLocalSize;

    public MethodWriter(String access, String name, String descriptor) {
        this.access = access;
        this.name = name;
        this.descriptor = descriptor;
    }

    @Override
    public void visitMaxs(int maxStackSize, int maxLocalSize) {
        this.maxLocalSize = maxLocalSize;
        this.maxStackSize = maxStackSize;
    }

}
复制代码

Dans la classstructure de fichiers, la table de champs peut avoir zéro ou plusieurs champs, et la table de méthodes peut avoir une ou plusieurs méthodes, nous devons donc utiliser un tableau pour stocker la table de champs et la table de méthodes. Étant donné que chaque méthode ou chaque champ de la table des méthodes et de la table des champs est une structure de données, la table des champs et la table des méthodes stockent les visiteurs de champ et les visiteurs de méthode. Maintenant, nous écrivons la classe d'implémentation de l'interface visiteur de classe ClassWriter, le code est le suivant.

@Getter
public class ClassWriter implements ClassVisitor {

    private int version;
    private String className;
    private String access;
    private List<FieldWriter> fieldWriters = new ArrayList<>();
    private List<MethodWriter> methodWriters = new ArrayList<>();

    @Override
    public void visit(int version, String access, String className) {
        this.version = version;
        this.className = className;
        this.access = access;
    }

    @Override
    public FieldVisitor visitField(String access, String name, String descriptor) {
        FieldWriter fieldWriter = new FieldWriter(access, name, descriptor);
        fieldWriters.add(fieldWriter);
        return fieldWriter;
    }

    @Override
    public MethodVisitor visitMethod(String access, String name, String descriptor) {
        MethodWriter methodWriter = new MethodWriter(access, name, descriptor);
        methodWriters.add(methodWriter);
        return methodWriter;
    }
    
}
复制代码

La visitFieldméthode de visiteur de classe ajoute d'abord un élément de champ à la classe, crée le visiteur de champ FieldVisitoret ajoute le visiteur de champ à la table de champ, puis renvoie finalement le visiteur de champ. La visitMethodméthode visiteur de classe ajoute d'abord un élément de méthode à la classe, crée le visiteur de méthode MethodVisitoret ajoute le visiteur à la table de méthodes, puis renvoie finalement le visiteur de méthode.

Dans ASMle cadre, peut être appelé ClassWriterle toByteArrayprocédé d'obtention de la classe générée classtableau d'octets, on peut simuler mis en oeuvre la toByteArrayméthode, l' ClassWriteradjonction showClassprocédé illustré dans le code suivant.

    public void showClass() {
        System.out.println("版本号:" + getVersion());
        System.out.println("访问标志:" + getAccess());
        System.out.println("类名:" + getClassName());

        for (FieldWriter fieldWriter : fieldWriters) {
            System.out.print(fieldWriter.getAccess()
                    + " " + fieldWriter.getDescriptor()
                    + " " + fieldWriter.getName()
                    + " 注解:");
            for (String annotation : fieldWriter.getAnnotations()) {
                System.out.println(annotation + " ");
            }
        }

        for (MethodWriter methodWriter : methodWriters) {
            System.out.println(methodWriter.getAccess()
                    + " " + methodWriter.getName()
                    + " " + methodWriter.getDescriptor()
                    + " 操作数栈大小:" + methodWriter.getMaxStackSize()
                    + " 局部变量表大小:" + methodWriter.getMaxLocalSize());
        }
    }
复制代码

Maintenant, nous utilisons le ASMcadre de version simple que nous avons écrit pour générer une classe, ajouter un champ à la classe et ajouter une note au champ, ajouter une méthode à la classe et définir la taille de la table de variables locales et la pile d'opérandes de la méthode, sous le code.

public static void main(String[] args) {
    ClassWriter classWriter = new ClassWriter();
    classWriter.visit(52, "public", "com.wujiuye.User");
    FieldVisitor fieldVisitor = classWriter
            .visitField("private", "name", "Ljava/lang/String;");
    fieldVisitor.visitAnnotation("@Getter", true);
    MethodVisitor methodVisitor = classWriter
            .visitMethod("public", "getName", "(Ljava/lang/String)V");
    methodVisitor.visitMaxs(1, 1);
    classWriter.showClass();
}
复制代码

Le résultat est le suivant

版本号:52
访问标志:public
类名:com.wujiuye.User
private Ljava/lang/String; name 注解:注解:@Getter,是否true 
public getName (Ljava/lang/String)V 操作数栈大小:1 局部变量表大小:1
复制代码

Je suppose que tu aimes

Origine juejin.im/post/5e9ae4f051882573b7538597
conseillé
Classement