This is a sample class loader using ClassPool. Unlike a regular class loader, this class loader obtains bytecode from a ClassPool.
Note that Javassist can be used without this class loader; programmers can define their own versions of class loader. They can run a program even without any user-defined class loader if that program is statically translated with Javassist. This class loader is just provided as a utility class.
Suppose that an instance of MyTranslator implementing the interface Translator is responsible for modifying class files. The startup program of an application using MyTranslator should be something like this:
import javassist.*; public class Main { public static void main(String[] args) throws Throwable { MyTranslator myTrans = new MyTranslator(); ClassPool cp = ClassPool.getDefault(); Loader cl = new Loader(cp); cl.addTranslator(cp, myTrans); cl.run("MyApp", args); } } Class MyApp is the main program of the application.
This program should be executed as follows:
% java Main arg1 arg2...
It modifies the class MyApp with a MyTranslator object before the JVM loads it. Then it calls main() in MyApp with arguments arg1, arg2, ...
This program execution is equivalent to:
% java MyApp arg1 arg2...
except that classes are translated by MyTranslator at load time.
If only a particular class must be modified when it is loaded, the startup program can be simpler; MyTranslator is unnecessary. For example, if only a class test.Rectangle is modified, the main() method above will be the following:
ClassPool cp = ClassPool.getDefault(); Loader cl = new Loader(cp); CtClass ct = cp.get("test.Rectangle"); ct.setSuperclass(cp.get("test.Point")); cl.run("MyApp", args); This program changes the super class of the test.Rectangle class.
Note 1:
This class loader does not allow the users to intercept the loading of java.* and javax.* classes (and sun.*, org.xml.*, ...) unless Loader.doDelegation is false. This is because the JVM prohibits a user class loader from loading a system class. Also see Note 2. If this behavior is not appropriate, a subclass of Loader must be defined and loadClassByDelegation() must be overridden.
Note 2:
If classes are loaded with different class loaders, they belong to separate name spaces. If class C is loaded by a class loader CL, all classes that the class C refers to are also loaded by CL. However, if CL delegates the loading of the class C to CL', then those classes that the class C refers to are loaded by a parent class loader CL' instead of CL.
If an object of class C is assigned to a variable of class C belonging to a different name space, then a ClassCastException is thrown.
Because of the fact above, this loader delegates only the loading of javassist.Loader and classes included in package java.* and javax.* to the parent class loader. Other classes are directly loaded by this loader.
For example, suppose that java.lang.String would be loaded by this loader while java.io.File is loaded by the parent class loader. If the constructor of java.io.File is called with an instance of java.lang.String, then it may throw an exception since it accepts an instance of only the java.lang.String loaded by the parent class loader.
@see javassist.ClassPool
@see javassist.Translator
To run a program, say MyApp, including a reflective class, you must write a start-up program as follows:
public class Main { public static void main(String[] args) throws Throwable { javassist.tools.reflect.Loader cl = (javassist.tools.reflect.Loader)Main.class.getClassLoader(); cl.makeReflective("Person", "MyMetaobject", "javassist.tools.reflect.ClassMetaobject"); cl.run("MyApp", args); } } Then run this program as follows:
% java javassist.tools.reflect.Loader Main arg1, ...
This command runs Main.main() with arg1, ... and Main.main() runs MyApp.main() with arg1, ... The Person class is modified to be a reflective class. Method calls on a Person object are intercepted by an instance of MyMetaobject.
Also, you can run MyApp in a slightly different way:
public class Main2 { public static void main(String[] args) throws Throwable { javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader(); cl.makeReflective("Person", "MyMetaobject", "javassist.tools.reflect.ClassMetaobject"); cl.run("MyApp", args); } } This program is run as follows:
% java Main2 arg1, ...
The difference from the former one is that the class Main is loaded by javassist.tools.reflect.Loader whereas the class Main2 is not. Thus, Main belongs to the same name space (security domain) as MyApp whereas Main2 does not; Main2 belongs to the same name space as javassist.tools.reflect.Loader. For more details, see the notes in the manual page of javassist.Loader.
The class Main2 is equivalent to this class:
public class Main3 { public static void main(String[] args) throws Throwable { Reflection reflection = new Reflection(); javassist.Loader cl = new javassist.Loader(ClassPool.getDefault(reflection)); reflection.makeReflective("Person", "MyMetaobject", "javassist.tools.reflect.ClassMetaobject"); cl.run("MyApp", args); } } Note:
javassist.tools.reflect.Loader does not make a class reflective if that class is in a java.* or javax.* pacakge because of the specifications on the class loading algorithm of Java. The JVM does not allow to load such a system class with a user class loader.
To avoid this limitation, those classes should be statically modified with javassist.tools.reflect.Compiler and the original class files should be replaced.
@see javassist.tools.reflect.Reflection
@see javassist.tools.reflect.Compiler
@see javassist.Loader
In order for a Loader implementation to successfully operate with a Context implementation that implements reloading, it must obey the following constraints:
Lifecycle so that the Context can indicate that a new class loader is required. start() method must unconditionally create a new ClassLoader implementation. stop() method must throw away its reference to the ClassLoader previously utilized, so that the class loader, all classes loaded by it, and all objects of those classes, can be garbage collected. stop() to be followed by a call to start() on the same Loader instance. Context.reload() method on the owning Context when a change to one or more of the class files loaded by this class loader is detected. Loader TODO
Loader class provides utility methods for the actual launchers to help launching the framework.
In order for a Loader implementation to successfully operate with a Container implementation that implements reloading, it must obey the following constraints:
Lifecycle so that the Container can indicate that a new class loader is required. start() method must unconditionally create a new ClassLoader implementation. stop() method must throw away its reference to the ClassLoader previously utilized, so that the class loader, all classes loaded by it, and all objects of those classes, can be garbage collected. stop() to be followed by a call to start() on the same Loader instance. Container.reload() method on the owning Container when a change to one or more of the class files loaded by this class loader is detected. | |
| |
| |
| |
| |
| |
| |
| |
| |
| |