The prerequisite to understand Jiapi reflection, is to be comfortable with Java reflection. Some knowledge about Java bytecode is also needed. Feel free to skip this section if you are more interested to utilize Jiapi in a higher level of abstraction.
Class
,
Method
and Field
. These abstractions can be
used to query reflective information about classes and interfaces. Jiapi
reflection extends this concept so that it is possible to alter the classes
and interfaces too.
Using JiapiClass
it is possible to query and modify the
class it represents. All bytecode modifications are done through
InstructionList
which represents the bytecode
Instructions
of a class.
Test.java:
package samples.reflect.hello1; import java.lang.reflect.Method; public class Test { public static void main(String args[]) throws Exception { Class cl = Test.class; Method m = cl.getMethod("helloWorld", new Class[] {}); HelloWorld hw = (HelloWorld) cl.newInstance(); m.invoke(hw, new Object[] {}); } }Please notice how
Test
gets a reference to its method
helloWorld. It also creates an instance of itself and casts
it to HelloWorld
. If you try to run this class you would
naturally get NoSuchMethodException
and
ClassCastException
. This example shows how you can add
these missing features to the class.
The HelloWorld
interface looks like:
package samples.reflect.hello1; public interface HelloWorld { public void helloWorld(); }The class modification is done in class
HelloWorldBuilder:
1 package samples.reflect.hello1; 2 3 import java.io.FileOutputStream; 4 import java.lang.reflect.Modifier; 5 import alt.jiapi.reflect.*; 6 7 public class HelloWorldBuilder { 8 public HelloWorldBuilder() throws Exception { 9 // Load the target class: 10 Loader loader = new Loader(); 11 JiapiClass clazz = loader.loadClass("samples.reflect.hello1.Test"); 12 13 // Add an interface to a clazz: 14 clazz.addInterface("samples.reflect.hello1.HelloWorld"); 15 16 // Add an empty method for a clazz. The method signature must 17 // match the method from HelloWorld interface, the only difference 18 // is that we want to implement the method now so it can't be abstract. 19 JiapiMethod method = clazz.addMethod(Modifier.PUBLIC, "void", 20 "helloWorld", new String[] {}); 21 22 // Then create the method body. The body will make a call to 23 // System.out.println and then just return. 24 InstructionList il = method.getInstructionList(); 25 InstructionFactory iFactory = il.getInstructionFactory(); 26 il.add(iFactory.getField(loader.loadClass("java.lang.System").getField("out"))); 27 il.add(iFactory.constant("hello world!")); 28 il.add(iFactory.invoke(System.out.getClass().getMethod("println", new Class[] { String.class }))); 29 il.add(iFactory.returnMethod(method)); 30 31 // Finalize the modified class and dump it to the file: 32 clazz.doPreDefine(); 33 clazz.dump(new FileOutputStream("Test.class")); 34 } 35 36 public static void main(String args[]) throws Exception { 37 new HelloWorldBuilder(); 38 } 39 }To run this example set your CLASSPATH as specified in README file. Then say:
java samples.reflect.hello1.HelloWorldBuilder
You should now see a Test.class
in your working directory.
To verify if the Test.class
works you can try to run it.
First make a required package hierarchy and move Test.class
there so that Java vitual machine finds it:
mkdir -p samples/reflect/hello1
mv Test.class samples/reflect/hello1/
Then add . as a first element in your CLASSPATH and launch Test
:
export CLASSPATH=.:$CLASSPATH
java samples.reflect.hello1.Test
You should see a line "hello world!" in your console instead of
NoSuchMethodException
or ClassCastException
.