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.