Android AOP programming -- Javassist Foundation

Posted by rkeppert on Fri, 21 Jan 2022 15:44:00 +0100

What is Javassist

This is Javassist official website Description on:

Javassist (Java programming assistant) makes Java bytecode operation simple. It is a class library for editing bytecode in Java; It enables Java programs to define a new class at run time and modify it when the JVM loads the class file. Unlike other similar bytecode editors, javassist provides two levels of APIs: source level and bytecode level. If users use source level APIs, they can edit class files without understanding the Java bytecode specification. The entire API is designed using only the vocabulary of the Java language. You can even specify the inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, bytecode level APIs allow users to edit class files directly like other editors.

The GitHub address of Javassist is: https://github.com/jboss-javassist/javassist

How to use

Generate new Java classes using Javassist

Here is an example to show the usage of Javassist:

This example uses IDEA as a development tool, creates a new Java project, creates a lib directory under the project root directory, and copies javassist. Java in the Lib directory Jar file and right-click - > Add as library to import the jar package. For the download of the jar package, you can click here .

Create a new class com. In IDEA test. javassist. Test and add the following code:

package com.test.javassist;

import javassist.*;

import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        // Create a Person class
        CtClass clz = pool.makeClass("com.test.javassist.Person");
        // Create a string type member variable named name
        CtField field = new CtField(pool.get("java.lang.String"), "name", clz);
        // Set the name member variable as a private property
        field.setModifiers(Modifier.PRIVATE);
        // Add the name member variable to the Person class
        clz.addField(field);
        // Provide setter and getter methods for the member variable name for the Person class
        clz.addMethod(CtNewMethod.setter("setName", field));
        clz.addMethod(CtNewMethod.getter("getName", field));
        // Add a parameterless constructor for the Person class
        CtConstructor defaultConstructor = new CtConstructor(new CtClass[]{}, clz);
        defaultConstructor.setBody("{name = \"\";}");
        clz.addConstructor(defaultConstructor);
        // Add a constructor with parameters to the Person class
        CtConstructor paramsConstructor = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, clz);
        // $0 means this, $1, $2 Representation method parameters
        paramsConstructor.setBody("{$0.name = $1;}");
        clz.addConstructor(paramsConstructor);
        // Create a sayHello method for the Person class
        CtMethod sayHello = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{}, clz);
        sayHello.setModifiers(Modifier.PUBLIC);
        sayHello.setBody("System.out.println(\"hello, this is \" + $0.name);");
        clz.addMethod(sayHello);
        // Write the Person class to the file. If there is no parameter, it will be written to the root directory of the current project
        clz.writeFile();
    }

}

After running the above code, a person will be generated in the current directory Class file, open it with IDEA, and you can see the following source code:


The following uses reflection to verify the correctness of the Person class generated by the above code:

// Testing the Person class with reflection
Class<?> aClass = Class.forName("com.test.javassist.Person");
Object personObj = aClass.newInstance();
Method setNameMethod = personObj.getClass().getDeclaredMethod("setName", String.class);
setNameMethod.invoke(personObj,"zhangsan");
personObj.getClass().getDeclaredMethod("sayHello").invoke(personObj);

After executing the above code, you can see the console output as follows:

This indicates that the Person class generated earlier using Javassist is correct.

Using Javassist to modify existing Java classes

Above, we created a Person class dynamically using Javassist. If you want to modify an existing Java class, you can use the following methods. Next, take the above Person class as an example and print a line of log in the sayHello method. The code implementation is as follows:

ClassPool pool = ClassPool.getDefault();
// Get the Person class
CtClass ctClass = pool.getCtClass("com.test.javassist.Person");
// Get the sayHello method of the Person class
CtMethod sayHello = ctClass.getDeclaredMethod("sayHello");
// Insert a line of code at the beginning of the method
sayHello.insertBefore("System.out.println(\"this log is inserted before call sayHello()\");");
// Write the file after modification, so as to check whether the modification is successful
ctClass.writeFile();

After executing the above code, we can find that a new com. Com has been automatically generated in the project root directory test. javassist. Person. Class file. The decompiled source code is as follows:


You can see that Javassist successfully modified the bytecode of the Person class.

Use of Javassist in Android

Javassist can directly manipulate bytecode, so it can be used for full buried point (non perceived buried point), hot repair, etc. in Android. Subsequent blog posts will record javassist's application in android in detail.

reference resources

Topics: Java Android AOP