Translation: Ithuriel Xiao
Summary
Javassist is a strong and powerful drive code library that performs a word code operation. It allows developers to freely add new methods in a class that has been compiled, or modify existing methods. However, and other similar libraries are different, JavaSST does not require how deep understanding of the byhenon code, and it also allows developers to ignore the details and structures of the modified class itself.
Bytecode drivers are often used to perform modifications for classes that have been compiled, or by automatically create execution classes, etc., etc. This requires the Biographical Code Engine to modify the program at runtime or compile. Some techniques are to enhance the existing Java classes that exist in the present, while others use it to use or generate some classes created by the system during runtime. For example, the JDO1.0 specification uses bytecode technology to process and precompliate the tables in the database, and then package into Java classes. Especially in the development of object-oriented system development, quite a number of frame systems use bytecode to make us better acquire the model and dynamics. Some EJB containers, such as JBoss projects, by generating and loading EJBs in operation, thus dramatically shortening the period of deploying EJB. This technology is so fascinating, so that there is also a standard java.lang.Reflect.Proxy class in JDK to perform related operations.
However, despite this, writing bytecodes is a fairly unwelcome heavy task for framework developers. Learning and using the bytecode is a certain extent like using assembly language. This makes most developers, although it can get quite benefits on the program, the difficulty it needs is enough to cool this enthusiasm. Not only that, the use of bytecode operations in the program also greatly reduces the readability and maintainability of the program.
This is a very good cream bread, but we can only flow in the window ... Isn't we so?
Fortunately, we have javassist. Javassist is a library that can perform a zodical operation, but despite this, it is simple and understood. He allows developers to perform the operation of the bytecode layer for their own procedures, of course, you don't need to know how deep the bytecode, or you don't need to understand.
API Parallel to the Reflection API
The API in the outermost API of Javassist is quite similar. It allows you to easily view the structure of the class before loading ClassLoder. It consists primarily by CTClass, Ctmethod,, and CTfield. To perform and JDK reflection API Java.lang.class, Java.lang.Reflect.Method, Java.lang.Reflect.Method .field the same operation. These classes allow you to easily get its structure, function, and properties before loading the target class. In addition, not only in terms of function, even in structure, these classes are also substantially the same as those of the reflected API. For example, GetName, Getsuperclass, GetMethods, GetSignature, and more. If you know about Java's reflex mechanism, this layer using Javassist will be relaxed and happy.
Next we will give an example of information about using javassist to read org.geometry.point.class (of course, don't forget to introduce javassist. * Pack):
1. ClassPool pool = classpool.getDefault ();
2. CTClass Pt = pool.get ("Org.Geometry.Point"); 3. System.out.println (pt.getsuperclass (). Getname ());
Among them, ClassPool is a creation plant of CTCLASS. It finds the location of the CTClass in the Class Path and creates a CTClass instance for each analysis request. And "getsuperclass (). GetName ()" shows the name of the parent class inherited by Org.Geometry.Point.class.
However, the API reflected is that Javassist does not provide constructive ability. In other words, we can't get an instance of an org.geometry.point.class class. On the other hand, before this class is not instantiated, Javassist does not provide a method of calling the interface of the target class and the value of the value of the property. In the analysis phase, it only provides customization of class definitions of the target class, and this is not possible to reflect the API.
For example, as follows:
4. Pt.setsuperClass (Pool.Get ("Figure");
This will modify the relationship between the target class and its parent classes. We will make Org.geometry.Point.clas inherit in the Figure class. Of course, in terms of consistency, it is necessary to ensure compatibility between the Figure class and the original parent class.
New ways to the target class are more simple. First we come to see how the word code is formed:
5. Ctmethod M = CtnewMethod.make ("Public INMOVE (INT DX) {x = DX;}", PT);
6. pt.addmethod (m);
The CTMETHOD class lets us add a method only to write a small function. This can be a good news that developers should write a large sequence of virtual machine instructions in order to implement such a small operation. Javassist will use a compiler itself to help us complete all this.
Finally, don't forget to indicate javassist to deposit the name code that has been written into your target class:
7. pt.writefile ();
The WriteFile method can help us write a modified definition to the.class file of the target class. Of course, we can even get all this time when you load this target class, Javassist can work with ClassLoader, and we will see this shortly.
Javassist is not the first to complete the translation from the code to byte code. Jakarta's BCEL is also a relatively well-known bytecode engine tool. However, you can't use BCEL to complete the code level of the code level. If you need to add a new method in a class that has been compiled, if you use BCEL, you can only define a command sequence composed of such a large string word code. As mentioned above, this is not what we want to see. Therefore, in this respect, Javassis uses the code to insert a new method is a big gospel.
INSTRUMENTING A METHOD BODY
Similarly, other operations for a class of methods are also defined on the code layer. In other words, although these steps are necessary, developers also do not need to perform and modify the virtual machine's instruction sequence, and Javassis will automatically complete these operations. Of course, if the developer believes that it is necessary to manage and monitor these steps, or if you want to manage these operations, JavaSSIST also provides a more underlying API to achieve, but we will not be here in this article. The topic is again discussed. Well, despite the structure, it is similar to the BCEL's bytecode layer API. Design Javassist's operational API's operation API for the child correspondence of the target class is based on Aspect-Oriented Programming (AOP) idea. JavaSST allows statements with coupling relationships as a whole, which allows for calling or acquiring other functions or attribute values in an insert statement. It will automatically decompose these statements and perform nested operations.
As shown in the following example, Listing 1 first contains a CTMETHOD that is primarily awarded the DRAW method of the Screen class. Then, we define a Point class that has a Move operation that implements the move of the Point. Of course, before the move, we hope to get the power of the Point through the Draw method, then we need to increase the following definitions on the MOVE method:
{System.out.println ("Move"); $ _ = $ proceed ($$);
This way, we can print out its position before performing Move. Note that the calling statement here is as follows:
$ _ = $ proceed ($$);
This way we will track the position of the POINT in the original CTMethod class.
Base and as above, CTMethod's operation of Methord is actually divided into the following steps, first, CTMETHOD's MethORD will scan the insert statement (code) itself. Once the subfunction is found, an Expreditor instance is created to analyze and execute the operation of this subunies. This operation will be completed before the entire plug in the statement. And if this instance exists attributes of a Static, Methord will take the lead in detecting the insert statement. Then, the Static property will automatically replace all the relevant parts in the insert statement (code) in the POINT class inserted to the target class --- as the above example. However, it is worth noting that the above alternative operation will be completed after javassist transitions the insert statement (code) into the bytecode.
Special Variables
In the replacement statement (code), we also have some special variables to complete the call to a sub-function, and this time we need to use the keyword "$". In Javassist, "$" is used to stipulate a special parameter, and "$ _" is used to declare a reply value of the function. Each special parameter should be this look "$ 1, $ 2, $ 3 ...", but especially, the target class itself is represented as "$ 0" when it is called. This use format makes developers a lot when filling out parameters using the child function. For example, as follows:
{System.out.println ("Move"); $ _ = $ proced ($ 1, 0);
Note that the second parameter of the sub function is 0.
Another special type is $ arg, which is actually an Object queue that accommodates all the call parameters of the function. When Javassist is scanning the $ ARG, if a certain parameter is found to be the basic type of Java, it will automatically pack the parameter and put it into the queue. For example, when it finds that a parameter is an int type, it will use the Java.lang.integer class to package this int parameter and store the parameter queue. And Java's reflection package: the invoke method in the java.lang.reflect.MethOrd class, $ ARGS is clearly saved. Javassist also allows developers to insert a certain statement (code) on a function of a function or a function. For example, it has an INSERTBEFORE method to perform an action before calling a function, and its use is roughly this:
1. ClassPool Pool = ClassPool.getDefault (); 2. CTClass CC = pool.get ("screen"); 3. CTMethod cm = cc.getDeclaredMethod ("DRAW", new ctclass [0]); 4. Cm.insertbefore ("{System.out.println ($ 1); system.out.println ($ 2);}"); 5. Cc.writefile ();
The above example allows us to perform print operations before the Draw function call - prints two parameters passed to DRAW.
Similarly, we can also use keyword $ to modify a function or package,
1. CTClass cc = sloader.get ("Point"); 2. Ctmethod m1 = cc.getDeclaredMethod ("Move"); 3. CTMethod M2 = CtnewMethod.copy (M1, CC, NULL); 4. M1.setname M1.GetName () "_orig"); 5. m2.SetBody ("{system.out.println (" call "); Return $ proceed ($$);}," this ", m1.getname () ); 6. Cc.addmethod (m2); 7. Cc.writefile ();
The first four lines of the above code are not difficult to understand, Javassist first made a copy of the MOVE method in Point and created a new function. Then it renamed "_orig" with the original MOVE method in the Point class. Next, let us pay attention to several parameters in the fifth line of the program: The first parameter indicates that the first part of the function needs to print a piece of information first, then execute the child function proceed () and return the result, this and MOVE method Almost, very well understood. The second parameter is just the location of the class of the subunies. Here is this is the Point class itself. The third parameter, that is, "m1.getname ()" defines the name of this new function.
Javassist also has other operations and classes to help you implement values such as modifying a value, changing the functionality of the function, and makes the function of other operations after the execution of a function. You can browse www.javassist.org to get relevant information.
Page two hours, say ~~~ English is too bad, but this thing is true, especially combined with reflection, super spirit, there may be a lot of mistakes, my official work experience is less than a year Please advise.