[JAVA programming] JAVA assist summary

Keywords: Java jvm Tomcat

Javaassist reference manual

1. Read and output bytecode

ClassPool pool = ClassPool.getDefault();
//The class will be queried from the classpath
CtClass cc = pool.get("test.Rectangle");
//Set the parent class of. Rectangle
cc.setSuperclass(pool.get("test.Point"));
//Output the. Rectangle.class file to this directory
cc.writeFile("c://");
//Output to binary format
//byte[] b=cc.toBytecode();
//Output and load the class class, which is loaded into the ClassLoader of the current thread by default, or select the output ClassLoader.
//Class clazz=cc.toClass();

As you can see here, Javassist is loaded by ClassPool class, and its output mode supports three kinds.

2. New Class

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Point");
//New method
cc.addMethod(m);
//New Field
cc.addField(f);
As can be seen from the above, the modification of Class mainly depends on the Class of CtClass. API is also clear and simple.

3. Freeze Class

When CtClass calls writeFile(), toClass(), toBytecode(), Javassist will freeze the CtClass object, and the modification of the CtClass object is not allowed. This is mainly to warn the developer that the class has been loaded, and the JVM does not allow the class to be reloaded. If you want to break through this limitation, you can do it as follows:
CtClasss cc = ...;
    :
cc.writeFile();
cc.defrost();
cc.setSuperclass(...);    // OK since the class is not frozen.

When ClassPool. Doppruning = true, when javassist is frozen in the Cclass object, the data stored in ClassPool will be released. This can reduce the memory consumption of javassist. By default, ClassPool. Doppruning = false. for example

CtClasss cc = ...;
cc.stopPruning(true);
    :
cc.writeFile();                             // convert to a class file.
//cc not released

Tip: when debugging, you can call debugWriteFile(), which will not cause CtClass to be released.

4. Class search path

As can be seen from the above Class The load of depends on ClassPool,and ClassPool.getDefault() Method search Classpath Just searchJVMUnder the same path ofclass. When a program runs in JBoss perhaps Tomcat Next, ClassPool Object User's may be found classes. Javassist Four dynamic loads are provided classpath Method. as follows

//The default loading method is pool.insertclasspath (New classclasspath (this. Getclass());
ClassPool pool = ClassPool.getDefault();

//Load classpath from file
pool.insertClassPath("/usr/local/javalib")

//Load from URL
ClassPath cp = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
pool.insertClassPath(cp);

//Load from byte []
byte[] b = a byte array;
String name = class name;
cp.insertClassPath(new ByteArrayClassPath(name, b));

//class can be loaded from the input stream
InputStream ins = an input stream for reading a class file;
CtClass cc = cp.makeClass(ins);

5,ClassPool

5.1 Reduce memory overflow
     ClassPool It is a CtClass objects The load container for. When loaded CtClass object After that, it will not be ClassPool Released (by default). This is because CtClass object It may be used in the next stage.
     //When too many ctlass objects are loaded, the OutOfMemory exception will be caused. To avoid this exception, javassist provides several methods. One is the ClassPool mentioned above.doPruning This parameter, another method is called CtClass.detach()Method, you can CtClass object from ClassPool Remove from. For example:
CtClass cc = ... ;
cc.writeFile();
cc.detach();

//Another method is to use no default ClassPool, that is, no ClassPool.getDefault()This way to generate. So when ClassPool When it's not quoted,JVMThe garbage collection of will collect this class. for example
//ClassPool(true) will load the ClassPath of the Jvm by default
ClassPool cp = new ClassPool(true);
// if needed, append an extra search path by appendClassPath()

5.2  cascade ClassPools
     javassist Supports cascading ClassPool,It is similar to inheritance. For example:
ClassPool parent = ClassPool.getDefault();
ClassPool child = new ClassPool(parent);
child.insertClassPath("./classes");

5.3 Modified existing Class Of name To create a new Class
//When the setName method is called, the Class name of the existing Class will be modified directly. If the old Class name is used again, it will be loaded under the classpath path path again. For example:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.setName("Pair");
//Reload in classpath
CtClass cc1 = pool.get("Point");

//For a Frozen)Of CtClass object ,It can't be modifiedclass name, you can reload if you need to modify, for example:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Point");
cc.writeFile();
//cc.setName("Pair");    wrong since writeFile() has been called.
CtClass cc2 = pool.getAndRename("Point", "Pair");
 

6,Class loader

    As mentioned above, javassist Same Class Can't be in the same place ClassLoader Loaded twice in. So in the output CtClass Pay attention to the following, for example:
// When Hello is not loaded, the following can be run.
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("Hello");
Class c = cc.toClass();
//In this case, there will be an error because Hello2 is loaded
Hello2 h=new Hello2();
CtClass cc2 = cp.get("Hello2");
Class c2 = cc.toClass();//java.lang.LinkageError exception will be thrown here
//To solve the loading problem, you can specify an unloaded ClassLoader
Class c3 = cc.toClass(new MyClassLoader());

6.1 Use javassist.Loader
//As can be seen from the above, if the Class is loaded twice in the same classloader and throws an exception, a classloader is also provided for javassist to use, for example
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass ct = pool.get("test.Rectangle");
ct.setSuperclass(pool.get("test.Point"));
Class c = cl.loadClass("test.Rectangle");
Object rect = c.newInstance();
        :
//To facilitate monitoring the life cycle of ClassLoader provided by javassist, javassist also provides a listener, which can monitor the life cycle of ClassLoader, for example:
//Translator is the monitor
public class MyTranslator implements Translator {
   void start(ClassPool pool)
       throws NotFoundException, CannotCompileException {}
   void onLoad(ClassPool pool, String classname)
       throws NotFoundException, CannotCompileException
   {
       CtClass cc = pool.get(classname);
       cc.setModifiers(Modifier.PUBLIC);
   }
}
//Example
public class Main2 {
 public static void main(String[] args) throws Throwable {
    Translator t = new MyTranslator();
    ClassPool pool = ClassPool.getDefault();
    Loader cl = new Loader();
    cl.addTranslator(pool, t);
    cl.run("MyApp", args);
 }
}
//output
% java Main2 arg1 arg2...

6.2 Modify system Class
//fromJVMAccording to the specification, system classloader It is better than others. classloader Is loaded first, and system classloader Mainly loading system Class,So we need to modify the system Class,It is impossible to modify the program if it runs with default parameters. If you need to modify it, you can add it at run time-Xbootclasspath/p: The meaning of parameters can refer to other documents. Modify below String An example is as follows:
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("java.lang.String");
CtField f = new CtField(CtClass.intType, "hiddenValue", cc);
f.setModifiers(Modifier.PUBLIC);
cc.addField(f);
cc.writeFile(".");
//Run script
% java -Xbootclasspath/p:. MyApp arg1 arg2...

6.3 Dynamic overload Class
//IfJVMRun time onJPDA(Java Platform Debugger Architecture),be Class Is dynamically reloaded by the run. For details, please refer to java.lang.Instrument. javassist An runtime overload is also provided Class The specific method can be seenAPI Medium javassist.tools.HotSwapper. 

7. Introduction and customization

Javassist encapsulates many very convenient methods for use, most of which only need to use these APIs. If not, javassist also provides a low-level API (refer to javassist.bytecode package for details) to modify the original Class.

7.1 insert source text before or after method body
 CtMethod and CtConstructor provide methods insertBefore(), insertAfter(), and addCatch(), which can insert a souce text to the corresponding location of the existing method. javassist includes a simple compiler to parse the souce text into binary and insert it into the corresponding method body.
     javassist also supports inserting a code segment to a specified number of lines, provided that the number of lines needs to be included in the class file.
     The inserted source can be associated with fields and methods, or with parameters of methods. However, when associating method parameters, you need to add the - g option at program compilation time (this option can save the declaration of local variables in the class file, which is not added by default). Because this parameter is not added by default, Javassist also provides some special variables to represent method parameters: $1,.,$args... Note that the inserted source text cannot refer to the declaration of method local variables, but it is allowed to declare a new method local variable, unless the - g option is added when the program is compiled.
Special variable description of the method:
$0, $1, $2, ...	this and actual parameters
$args	An array of parameters. The type of $args is Object[].
$$	All actual parameters.For example, m($$) is equivalent to m($1,$2,...)
$cflow(...)	cflow variable
$r	The result type. It is used in a cast expression.
$w	The wrapper type. It is used in a cast expression.
$_	The resulting value
$sig	An array of java.lang.Class objects representing the formal parameter types
$type	A java.lang.Class object representing the formal result type.
$class	A java.lang.Class object representing the class currently edited.

7.1.1 $0, $1, $2, ...
$0 code is this, $1 represents the first parameter of the method parameter, $2 represents the second parameter of the method parameter, and so on, $N represents the nth parameter of the method parameter. For example:
//Practical methods
void move(int dx, int dy) 
//javassist
CtMethod m = cc.getDeclaredMethod("move");
//Print dx, and dy
m.insertBefore("{ System.out.println($1); System.out.println($2); }");
Note: if javassist changes the value of $1, the actual parameter value will also change.

7.1.2 $args
 $args refers to an array of all parameters of a method, similar to Object []. If a parameter contains a basic type, it will be converted to its wrapper type. Note that $args[0] corresponds to $1, not $0, $0!=$args[0], $0=this.

7.1.3 $$
$$is a shorthand for all method parameters, mainly used in method calls. For example:
/ / original method
move(String a,String b)
move($$) is equivalent to move( ,)
If you add a new method with all the parameters of move, you can write:
exMove($$, context) is equivalent to exMove( , , context)

7.1.4 $cflow
 $cflow, which means control flow, is a read-only variable whose value is the depth of a method call. For example:
/ / original method
int fact(int n) {
    if (n <= 1)
        return n;
    else
        return n * fact(n - 1);
}

//javassist call
CtMethod cm = ...;
//cflow is used here
cm.useCflow("fact");
//cflow is used here to show that when the depth is 0, it is the first parameter to print the method when the first face method is called
cm.insertBefore("if ($cflow(fact) == 0)"
              + "    System.out.println(\"fact \" + $1);");

7.1.5 $r
 It refers to the type of method return value, which is mainly used for type transformation. For example:
Object result = ... ;
$_ = ($r)result;
If the return value is a wrapper type of basic type, the value will be automatically converted to basic type. If the return value is Integer, then $r is int. If the return value is void, the value is null.

7.1.6 $w
 $w represents a packaging type. Mainly used for transformation. For example: Integer i = ($w)5; if the type is not a basic type, it will be ignored.

7.1.7 $_
$? Represents the return value of the method.

7.1.8 $sig
 $sig refers to the type (Class) array of method parameters. The order of the array is the order of the parameters.

7.1.9 $class
 $Class refers to the type (Class) of this. That is, the type of $0.

7.1.10 addCatch()
addCatch() means to add a try catch block to a method. The main thing is to add a return value to the inserted code. $e for outliers. For example:
CtMethod m = ...;
CtClass etype = ClassPool.getDefault().get("java.io.IOException");
m.addCatch("{ System.out.println($e); throw $e; }", etype);
The actual code is as follows:
try {
    the original method body
}
catch (java.io.IOException e) {
    System.out.println(e);
    throw e;
}

8. Modify method body

CtMethod and CtConstructor Provided setBody() Can replace all contents in the method or constructor.
//Supported variables are:
$0, $1, $2, ...	this and actual parameters
$args	An array of parameters. The type of $args is Object[].
$$	All actual parameters.For example, m($$) is equivalent to m($1,$2,...)
$cflow(...)	cflow variable
$r	The result type. It is used in a cast expression.
$w	The wrapper type. It is used in a cast expression.
$sig	An array of java.lang.Class objects representing the formal parameter types
$type	A java.lang.Class object representing the formal result type.
$class	A java.lang.Class object representing the class currently edited.

//Note that $? Variables are not supported.

8.1 The source
javassist Allows modification of one of the expressions in a method. javassist.expr.ExprEditor thisclass You can replace the expression. For example:
CtMethod cm = ... ;
cm.instrument(
    new ExprEditor() {
        public void edit(MethodCall m)
                      throws CannotCompileException
        {
            if (m.getClassName().equals("Point")
                          && m.getMethodName().equals("move"))
                m.replace("{ $1 = 0; $_ = $proceed($$); }");
        }
    });

//Note: that the substituted code is not an expression but a statement or a block. It cannot be or contain a try-catch statement.

Method instrument() It can be used to search the content in the method body. For example, calling a method, field Access, object creation, etc. If you want to insert a method before or after an expression, modify the souce As follows:
{ before-statements;
  $_ = $proceed($$);
  after-statements; }

8.2 javassist.expr.MethodCall
MethodCall Represents a method call. use replace()Method can replace the called method.

$0	The target object of the method call.
This is not equivalent to this, which represents the caller-side this object.
$0 is null if the method is static.
$1, $2, ...
The parameters of the method call.
$_	The resulting value of the method call.
$r	The result type of the method call.
$class	A java.lang.Class object representing the class declaring the method.
$sig	An array of java.lang.Class objects representing the formal parameter types
$type	A java.lang.Class object representing the formal result type.
$proceed	The name of the method originally called in the expression.

//Note: $w, $args and $$It's also allowed. $0Nothis,Method only Object. $proceed It refers to a special grammar, not a String. 

8.3 javassist.expr.ConstructorCall
ConstructorCall Refers to a constructor, such as:this(),super()Call. ConstructorCall.replace()It is used to replace a block when the constructor is called.
$0	The target object of the constructor call. This is equivalent to this.
$1, $2, ...	The parameters of the constructor call.
$class	A java.lang.Class object representing the class declaring the constructor.
$sig	An array of java.lang.Class objects representing the formal parameter types.
$proceed	The name of the constructor originally called in the expression.

$w, $args and $$  It's also allowed.

8.4 javassist.expr.FieldAccess
FieldAccess The representative is Field Access class for.
$0	The object containing the field accessed by the expression. This is not equivalent to this.
this represents the object that the method including the expression is invoked on.
$0 is null if the field is static.
$1
The value that would be stored in the field if the expression is write access.
Otherwise, $1 is not available.
$_	The resulting value of the field access if the expression is read access.
Otherwise, the value stored in $_ is discarded.
$r	The type of the field if the expression is read access.
Otherwise, $r is void.
$class	A java.lang.Class object representing the class declaring the field.
$type
A java.lang.Class object representing the field type.
$proceed	The name of a virtual method executing the original field access. .

$w, $args and $$  It's also allowed.

8.5 javassist.expr.NewExpr
NewExpr It represents a Object Operation(But does not include the creation of arrays). 
$0	null
$1, $2, ...
The parameters to the constructor.
$_	The resulting value of the object creation.
A newly created object must be stored in this variable.
$r	The type of the created object.
$sig	An array of java.lang.Class objects representing the formal parameter types
$type	A java.lang.Class object representing the class of the created object.
$proceed	The name of a virtual method executing the original object creation. .

$w, $args and $$  It's also allowed.

8.6 javassist.expr.NewArray
NewArray Represents the creation of an array.
$0	null
$1, $2, ...
The size of each dimension.
$_	The resulting value of the object creation. 
A newly created array must be stored in this variable.
$r	The type of the created object.
$type	A java.lang.Class object representing the class of the created array .
$proceed	The name of a virtual method executing the original array creation. .

$w, $args and $$  It's also allowed.
//For example:
String[][] s = new String[3][4];
 $1 and $2 The value is 3 and 4, $3 Not available.
String[][] s = new String[3][];
 $1 The value is 3 ,but $2 Not available.

8.7 javassist.expr.Instanceof
Instanceof The representative is Instanceof Expression.
$0	null
$1
The value on the left hand side of the original instanceof operator.
$_	The resulting value of the expression. The type of $_ is boolean.
$r	The type on the right hand side of the instanceof operator.
$type	A java.lang.Class object representing the type on the right hand side of the instanceof operator.
$proceed	The name of a virtual method executing the original instanceof expression.
It takes one parameter (the type is java.lang.Object) and returns true
if the parameter value is an instance of the type on the right hand side of
the original instanceof operator. Otherwise, it returns false.
$w, $args and $$  It's also allowed.

8.8 javassist.expr.Cast
Cast Represents a transformation expression.
$0	null
$1
The value the type of which is explicitly cast.
$_	The resulting value of the expression. The type of $_ is the same as the type
after the explicit casting, that is, the type surrounded by ( ).
$r	the type after the explicit casting, or the type surrounded by ( ).
$type	A java.lang.Class object representing the same type as $r.
$proceed	The name of a virtual method executing the original type casting.
It takes one parameter of the type java.lang.Object and returns it after
the explicit type casting specified by the original expression.
$w, $args and $$  It's also allowed.

8.9 javassist.expr.Handler
Handler It represents atry catch Statement.
$1	The exception object caught by the catch clause.
$r
the type of the exception caught by the catch clause. It is used in a cast expression.
$w	The wrapper type. It is used in a cast expression.
$type	A java.lang.Class object representing
the type of the exception caught by the catch clause.

9 add a new method or field

Javassist Allows developers to create a new method or constructor. Add a new method, for example:
CtClass point = ClassPool.getDefault().get("Point");
CtMethod m = CtNewMethod.make(
                 "public int xmove(int dx) { x += dx; }",
                 point);
point.addMethod(m);

//Call other methods in the method, for example:
CtClass point = ClassPool.getDefault().get("Point");
CtMethod m = CtNewMethod.make(
                 "public int ymove(int dy) { $proceed(0, dy); }",
                 point, "this", "move");
//The effect is as follows:
public int ymove(int dy) { this.move(0, dy); }
//Here is another new method provided by javassist (not understood):
Javassist provides another way to add a new method. You can first create an abstract method and later give it a method body:

CtClass cc = ... ;
CtMethod m = new CtMethod(CtClass.intType, "move",
                          new CtClass[] { CtClass.intType }, cc);
cc.addMethod(m);
m.setBody("{ x += $1; }");
cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
Since Javassist makes a class abstract if an abstract method is added to the class, you have to explicitly change the class back to a non-abstract one after calling setBody().

9.1 Recursive Method
CtClass cc = ... ;
CtMethod m = CtNewMethod.make("public abstract int m(int i);", cc);
CtMethod n = CtNewMethod.make("public abstract int n(int i);", cc);
cc.addMethod(m);
cc.addMethod(n);
m.setBody("{ return ($1 <= 0) ? 1 : (n($1 - 1) * $1); }");
n.setBody("{ return m($1); }");
cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);

9.2 Newly added field
//As follows:
CtClass point = ClassPool.getDefault().get("Point");
CtField f = new CtField(CtClass.intType, "z", point);
point.addField(f);
//point.addField(f, "0");    // initial value is 0.
//Or:
CtClass point = ClassPool.getDefault().get("Point");
CtField f = CtField.make("public int z = 0;", point);
point.addField(f);

9.3 Remove method or field
call removeField()perhaps removeMethod(). 

10 notes

Get annotation information:
//annotation
public @interface Author {
    String name();
    int year();
}
//javassist code
CtClass cc = ClassPool.getDefault().get("Point");
Object[] all = cc.getAnnotations();
Author a = (Author)all[0];
String name = a.name();
int year = a.year();
System.out.println("name: " + name + ", year: " + year);

11 javassist.runtime

12 import

Reference package:
ClassPool pool = ClassPool.getDefault();
pool.importPackage("java.awt");
CtClass cc = pool.makeClass("Test");
CtField f = CtField.make("public Point p;", cc);
cc.addField(f);

## 13 limit

(1)I won't support it java5.0New syntax for. Annotation modification is not supported, but can be done through the underlying javassist Class, refer to: javassist.bytecode.annotation
(2)Initialization of arrays is not supported, such as String[]{"1","2"},Unless only the capacity of the array is1
(3)Inner and anonymous classes are not supported
(4)I won't support itcontinueand btreak Expression.
(5)Some do not support inheritance relationships. for example
class A {} 
class B extends A {} 
class C extends B {} 

class X { 
    void foo(A a) { .. } 
    void foo(B b) { .. } 
}
//If x is called.foo(new C()),May call foo(A) . 

(6)Recommended for developers#Separate oneclass nameandstatic method perhaps static field. For example:
javassist.CtClass.intType.getName()Recommended use javassist.CtClass#intType.getName()


## 13 BottomAPI


## 14 debug
//You can set a folder, generated by javassistclassWill be saved under this folder. For example: CtClass.debugDump = "./dump"; default debugDump=null.

58 original articles published, praised 6, 10000 visitors+
Private letter follow

Posted by erupt on Tue, 11 Feb 2020 22:05:12 -0800