Javassist realiza la compilación dinámica del código de bytes de Java

Javassist es una biblioteca de clases que se utiliza para procesar el código de bytes de Java. El código de bytes de Java generalmente se almacena en un archivo binario con un sufijo de clase. Cada archivo binario contiene una clase Java o una interfaz Java.
Javassist involucra principalmente varias clases como ClassPool, CtClass, CtMethod, CtConstructor, CtField, etc. Veamos el código y los comentarios para entender el significado de todo tipo:
Primero agregue la dependencia Javassist en pom.xml:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.27.0-GA</version>
</dependency>

1. Agregue una nueva clase y guárdela:

public static void  buildUser() throws Exception {
    
    
        ClassPool pool = ClassPool.getDefault();
        //导入包,告诉编译器在解析类名时搜索其他包
        //pool.importPackage("ml.code.UserUtils");
        //创建一个空类
        CtClass clazz = pool.makeClass("ml.code.User");
        //继承类
       // clazz.setSuperclass(pool.get("ml.code.UserUtils"));
        //新增一个字段 private String name
        CtField ctField = new CtField(pool.get("java.lang.String"), "name", clazz);
        // 访问级别是 private
        ctField.setModifiers(Modifier.PRIVATE);
        // 初始值是 "毛毛"
        clazz.addField(ctField, CtField.Initializer.constant("毛毛3"));

        // 3. 生成 getter、setter 方法
        clazz.addMethod(CtNewMethod.setter("setName", ctField));
        clazz.addMethod(CtNewMethod.getter("getName", ctField));

        // 4. 添加无参的构造函数
        CtConstructor cons = new CtConstructor(new CtClass[]{
    
    }, clazz);
        cons.setBody("{name = \"小张\";}");
        clazz.addConstructor(cons);

        // 5. 添加有参的构造函数
        cons = new CtConstructor(new CtClass[]{
    
    pool.get("java.lang.String")}, clazz);
        // $0=this / $1,$2,$3... 代表方法参数
        cons.setBody("{$0.name = $1;}");
        clazz.addConstructor(cons);

        // 6. 创建一个名为printName方法,无参数,无返回值,输出name值
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printName", new CtClass[]{
    
    }, clazz);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");
        clazz.addMethod(ctMethod);

        //这里会将这个创建的类对象编译为.class文件,生成之后在Java\IdeaProjects\comm\esa2000\target\classes\\ml\\code这个文件夹下面可以找到编译后的类
        //保存时此处的路径会自动加上pool.makeClass("ml.code.User")中的包路径
        clazz.writeFile();
       // clazz.writeFile("E:\\Java\\IdeaProjects\\comm\\target\\classes");
       // pool.appendClassPath("E:\\Java\\IdeaProjects\\comm\\target\\classes\\");
        System.out.println(clazz.isFrozen());
        pool=null;
        pool=ClassPool.getDefault();
        clazz = pool.get("ml.code.User");
        // 这里不写入文件,直接实例化
        Object person = clazz.toClass().newInstance();
        // 设置值
        Method setName = person.getClass().getMethod("setName", String.class);
        setName.invoke(person, "小王3");
        // 输出值
        Method execute = person.getClass().getMethod("printName");
        execute.invoke(person);
    }

Si usa otras clases en la nueva clase, pool.importPackage("ml.code.UserUtils");primero debe importar el paquete;
si el objeto CtClass se convierte en un objeto de clase mediante writeFile (), toClass () o toBytecode (), Javassist congelará el objeto CtClass. No se permiten cambios posteriores a este objeto. La razón de esto es principalmente porque la JVM ha cargado la clase. Dado que la JVM en sí misma no admite operaciones de carga repetidas de la clase, no se permiten cambios.
Entonces, después de writeFile (), toClass () o toBytecode (), debe continuar modificando la clase, y luego llamo al código antes de llamar a pool.get ():

if(ctClass.isFrozen()){
    
    
    ctClass.defrost();
}

clazz.writeFile();El método es guardar directamente en el directorio de categorías de programas, lo que equivale a clazz.writeFile(".");guardar en el directorio de categorías de programas. La ubicación de generación específica se encuentra en la ruta del paquete de la clase recién agregada en el directorio de categorías.
Si modifica y guarda, puede usar la siguiente declaración para obtener la ruta classPath del programa

String path=clazz.toClass().getResource("/log4j2.xml").getPath();

Luego intercepte la ruta classPath en ruta. Tenga en cuenta que "/" no se utiliza, esta es una ruta relativa. Es decir, se obtendrá el nombre de la ruta del paquete donde se encuentra log4j2.xml.

Dos, modificar una clase

public static void modifyUser()throws Exception{
    
    
        ClassPool pool = ClassPool.getDefault();
        /*
         *由静态方法ClassPool.getDefault()返回的默认ClassPool与JVM有相同的扫描路径。
         * 如果程序在JBoss或者Tomcat之类的web应用程序中,
         * ClassPoll对象可能无法找到用户的类,因为这些Web应用服务器使用多个类加载器以及系统类加载器。
         * 在这种情况下,就必须注册额外的类扫描路径到ClassPool中。可以使用如下方式进行注册:
         * 参考:https://www.jianshu.com/p/7323e7bc9a3c
         */
       // pool.insertClassPath(new ClassClassPath(UserUtils.class.getClass()));

        CtClass clazz = pool.get("ml.code.User");
        System.out.println(clazz.isFrozen());
        //防止CtClass被精简,手动开启关闭精简,具体含义看最后引用文章
        clazz.stopPruning(true);
        CtMethod madeUser = clazz.getDeclaredMethod("madeUser");
        madeUser.insertBefore("System.out.println(\"制造开始前\");");
        madeUser.insertAfter("System.out.println(\"成功制造。。。。\");");


        //新增一个方法
        CtMethod ctMethod = new CtMethod(pool.get("java.lang.String"), "modifyAge", new CtClass[]{
    
    }, clazz);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(\"我的年龄是:20\");System.out.println(\"我的年龄是:20\");return \"ss\";}");
        clazz.addMethod(ctMethod);
        //clazz.writeFile();
        clazz.writeFile("E:\\Java\\IdeaProjects\\comm\\target\\classes");
      //  clazz.writeFile("E:\\Java\\IdeaProjects\\comm\\target\\classes");
        //clazz.detach();
       // clazz = clazz.;
      //ClassLoader l=  clazz.toClass().getClassLoader();
        Object user = clazz.toClass().newInstance();
        // 调用 personFly 方法
        Method personFlyMethod = user.getClass().getMethod("madeUser");
        personFlyMethod.invoke(user);
        //调用 joinFriend 方法
        Method execute = user.getClass().getMethod("modifyAge");
        execute.invoke(user);
    }

Se usa al agregar una nueva clase, se usa al CtClass clazz = pool.makeClass("ml.code.User");modificar una clase existenteCtClass clazz = pool.get("ml.code.User");

El código final en los dos ejemplos usa la reflexión para recuperar la clase modificada para la prueba de ejecución.
Mire las dos citas, están escritas con más detalle, una es una traducción simple en chino y la otra es el texto original.

https://www.cnblogs.com/scy251147/p/11100961.html
http://www.javassist.org/tutorial/tutorial.html

Supongo que te gusta

Origin blog.csdn.net/u011930054/article/details/107223308
Recomendado
Clasificación