Javassist realizes dynamic compilation of Java bytecode

Javassist is a class library used to process java bytecode. Java bytecode is generally stored in a binary file with a suffix of class. Each binary file contains a java class or java interface.
Javassist mainly involves several classes such as ClassPool, CtClass, CtMethod, CtConstructor, CtField, etc. Let's look at the code and comments to understand the meaning of all kinds:
First, add Javassist dependency in pom.xml:

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

1. Add a new class and save it:

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);
    }

If you use other classes in the new class, you need pool.importPackage("ml.code.UserUtils");to import the package first;
if the CtClass object is converted into a class object by writeFile(), toClass() or toBytecode(), Javassist will freeze the CtClass object. Any subsequent changes to this object are not allowed. The reason for this is mainly because the class has been loaded by the JVM. Since the JVM itself does not support repeated loading operations of the class, changes are not allowed.
So after writeFile(), toClass() or toBytecode(), you need to continue to modify the class, and then I call the code before calling pool.get():

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

clazz.writeFile();The method is to save directly in the program category directory, which is equivalent to clazz.writeFile(".");saving in the program category directory. The specific generation location is under the package path of the newly added class under the category directory.
If you modify and save, you can use the following statement to get the classPath path of the program

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

Then intercept the classPath path in path. Note that "/" is not used, this is a relative path. That is, the package path name where log4j2.xml is located will be obtained.

Two, modify a class

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);
    }

Used when adding a new class, used when CtClass clazz = pool.makeClass("ml.code.User");modifying an existing classCtClass clazz = pool.get("ml.code.User");

The final code in the two examples uses reflection to retrieve the modified class for execution testing.
Look at the two quotes, they are written in more detail, one is a simple translation in Chinese, and the other is the original text.

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

Guess you like

Origin blog.csdn.net/u011930054/article/details/107223308