123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <title>Javassist Tutorial</title>
- <link rel="stylesheet" type="text/css" href="brown.css">
- </head>
- <body>
-
- <b>
- <font size="+3">
- Getting Started with Javassist
- </font>
-
- <p><font size="+2">
- Shigeru Chiba
- </font>
- </b>
-
- <p><div align="right"><a href="tutorial2.html">Next page</a></div>
-
- <ul>1. <a href="#read">Reading bytecode</a>
- <br>2. <a href="#def">Defining a new class</a>
- <br>3. <a href="#pool">ClassPool</a>
- <br>4. <a href="#load">Class loader</a>
- <br>5. <a href="tutorial2.html#intro">Introspection and customization</a>
- </ul>
-
- <p><br>
-
- <a name="read">
- <h2>1. Reading bytecode</h2>
-
- <p>Javassist is a class library for dealing with Java bytecode.
- Java bytecode is stored in a binary file called a class file.
- Each class file contains one Java class or interface.
-
- <p>The class <code>Javassist.CtClass</code> is an abstract
- representation of a class file. A <code>CtClass</code> (compile-time
- class) object is a handle for dealing with a class file. The
- following program is a very simple example:
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.get("test.Rectangle");
- cc.setSuperclass(pool.get("test.Point"));
- cc.writeFile("test.Rectangle");
- </pre></ul>
-
- <p>This program first obtains a <code>ClassPool</code> object,
- which controls bytecode modification with Javassist.
- The <code>ClassPool</code> object is a container of <code>CtClass</code>
- object representing a class file.
- It reads a class file on demand for constructing a <code>CtClass</code>
- object and contains the constructed object until it is written out
- to a file or an output stream.
-
- <p>The <code>ClassPool</code> object is used to maintain one-to-one
- mapping between classes and <code>CtClass</code> objects. Javassist
- never allows two distinct <code>CtClass</code> objects to represent
- the same class unless two independent <code>ClassPool</code> are created.
- This is a significant feature for consistent program
- transformaiton. To create multiple
- instances of <code>ClassPool</code>, write the following code:
-
- <ul><pre>
- ClassPool cp = new ClassPool();
- cp.appendSystemPath(); // or append another path by appendClassPath()
- </pre></ul>
-
- <p>This creates a <code>ClassPool</code> object that behaves as the
- default <code>ClassPool</code> returned by
- <code>ClassPool.getDefault()</code> does.
- <code>ClassPool.getDefault()</code> is a singleton factory method
- provided for convenience.
-
- <p>If you have two <code>ClassPool</code> objects, then you can
- obtain, from each <code>ClassPool</code>, a distinct
- <code>CtClass</code> object representing the same class file. You can
- differently modify these <code>CtClass</code> objects to generate
- different versions of the class.
-
- <p>To modify the definition of a class, the users must first obtain a
- reference to the <code>CtClass</code> object representing that class.
- <code>get()</code> in <code>ClassPool</code> is used for this purpose.
- In the case of the program shown at the beginning, the
- <code>CtClass</code> object representing a class
- <code>test.Rectangle</code> is obtained from the
- <code>ClassPool</code> object and it is assigned to a variable
- <code>cc</code>. Then it is modified so that the superclass of
- <code>test.Rectangle</code> is changed into a class
- <code>test.Point</code>. This change is reflected on the original
- class file when <code>writeFile()</code> in <code>CtClass()</code> is
- finally called.
-
- <p><code>writeFile()</code> translates the <code>CtClass</code> object
- into a class file and writes it on a local disk.
- Javassist also provides a method for directly obtaining the
- modified bytecode. To obtain the bytecode, call <code>toBytecode()</code>:
-
- <ul><pre>
- byte[] b = cc.toBytecode();
- </pre></ul>
-
- <p>The default <code>ClassPool</code> returned
- by a static method <code>ClassPool.getDefault()</code>
- searches the same path that the underlying JVM (Java virtual machine) has.
- The users can expand this class search path if needed.
- For example, the following code adds a directory
- <code>/usr/local/javalib</code>
- to the search path:
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- pool.insertClassPath("/usr/local/javalib");
- </pre></ul>
-
- <p>The search path that the users can add is not only a directory but also
- a URL:
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- ClassPath cp = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
- pool.insertClassPath(cp);
- </pre></ul>
-
- <p>This program adds "http://www.javassist.org:80/java/" to the class search
- path. This URL is used only for searching classes belonging to a
- package <code>org.javassist</code>.
-
- <p>Furthermore, you can directly give a byte array
- to a <code>ClassPool</code> object
- and construct a <code>CtClass</code> object from that array. To do this,
- use <code>ByteArrayClassPath</code>. For example,
-
- <ul><pre>
- ClassPool cp = ClassPool.getDefault();
- byte[] b = <em>a byte array</em>;
- String name = <em>class name</em>;
- cp.insertClassPath(new ByteArrayClassPath(name, b));
- CtClass cc = cp.get(name);
- </pre></ul>
-
- <p>The obtained <code>CtClass</code> object represents
- a class defined by the class file specified by <code>b</code>.
- The <code>ClassPool</code> reads a class file from the given
- <code>ByteArrayClassPath</code> if <code>get()</code> is called
- and the class name given to <code>get()</code> is equal to
- one specified by <code>name</code>.
-
- <p>If you do not know the fully-qualified name of the class, then you
- can use <code>makeClass()</code> in <code>ClassPool</code>:
-
- <ul><pre>
- ClassPool cp = ClassPool.getDefault();
- InputStream ins = <em>an input stream for reading a class file</em>;
- CtClass cc = cp.makeClass(ins);
- </pre></ul>
-
- <p><code>makeClass()</code> returns the <code>CtClass</code> object
- constructed from the given input stream. You can use
- <code>makeClass()</code> for eagerly feeding class files to
- the <code>ClassPool</code> object. This might improve performance
- if the search path includes a large jar file. Since
- a <code>ClassPool</code> object reads a class file on demand,
- it might repeatedly search the whole jar file for every class file.
- <code>makeClass()</code> can be used for optimizing this search.
- The <code>CtClass</code> constructed by <code>makeClass()</code>
- is kept in the <code>ClassPool</code> object and the class file is never
- read again.
-
- <p>The users can extend the class search path. They can define a new
- class implementing <code>ClassPath</code> interface and give an
- instance of that class to <code>insertClassPath()</code> in
- <code>ClassPool</code>. This allows a non-standard resource to be
- included in the search path.
-
- <p><br>
-
- <a name="def">
- <h2>2. Defining a new class</h2>
-
- <p>To define a new class from scratch, <code>makeClass()</code>
- must be called on a <code>ClassPool</code>.
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.makeClass("Point");
- </pre></ul>
-
- <p>This program defines a class <code>Point</code>
- including no members.
-
- <p>A new class can be also defined as a copy of an existing class.
- The program below does that:
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.get("Point");
- cc.setName("Pair");
- </pre></ul>
-
- <p>This program first obtains the <code>CtClass</code> object
- for class <code>Point</code>. Then it gives a new name <code>Pair</code>
- to that <code>CtClass</code> object.
- If <code>get("Point")</code> is later called on the <code>ClassPool</code>
- object again, then a class file <code>Point.class</code> is read again and
- a new <code>CtClass</code> object for class <code>Point</code> is constructed
- again. See the followings:
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.get("Point");
- CtClass cc1 = pool.get("Point"); // cc1 is identical to cc.
- cc.setName("Pair");
- CtClass cc2 = pool.get("Pair"); // cc2 is identical to cc.
- CtClass cc3 = pool.get("Point"); // cc3 is not identical to cc.
- </pre></ul>
-
- <p>Once a <code>CtClass</code> object is converted into a class file
- by <code>writeFile()</code> or <code>toBytecode()</code>, Javassist
- rejects further modifications of that <code>CtClass</code> object.
- Hence, after the <code>CtClass</code> object representing <code>Point</code>
- class is converted into a class file, you cannot define <code>Pair</code>
- class as a copy of <code>Point</code> since executing <code>setName()</code>
- on <code>Point</code> is rejected.
-
- <p>To avoid this restriction, you should call <code>getAndRename()</code>
- in <code>ClassPool</code>. For example,
-
- <ul><pre>
- ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.getAndRename("Point", "Pair");
- </pre></ul>
-
- <p>If <code>getAndRename()</code> is called, the <code>ClassPool</code>
- reads <code>Point.class</code> for creating a new <code>CtClass</code>
- object representing <code>Pair</code> class. <code>getAndRename()</code>
- can be executed after <code>writeFile()</code> or <code>toBytecode()</code>
- is called on the the <code>ClassPool</code> representing <code>Point</code>
- class.
-
-
- <p><br>
-
- <a name="pool">
- <h2>3. ClassPool</h2>
-
- <p>
- A <code>ClassPool</code> object is a container of <code>CtClass</code>
- objects. Once a <code>CtClass</code> object is created, it is
- recorded in a <code>ClassPool</code> for ever. This is because a
- compiler may need to access the <code>CtClass</code> object later when
- it compiles source code that refers to the class represented by that
- <code>CtClass</code>. If the class definition represented by that
- <code>CtClass</code> object is different from that of the original class
- file, the compiler cannot correctly compile the source code without
- the <code>CtClass</code> object.
-
- <p>
- This specification of <code>ClassPool</code> may cause huge memory
- consumption if the number of <code>CtClass</code> objects becomes large.
- To avoid this problem, you can explicitly remove an unnecessary
- <code>CtClass</code> object from the <code>ClassPool</code>. If you
- call <code>detach()</code> on a <code>CtClass</code> object, then that
- <code>CtClass</code> object is removed from the <code>ClassPool</code>.
- For example,
-
- <ul><pre>
- CtClass cc = ... ;
- cc.writeFile();
- cc.detach();
- </pre></ul>
-
- <p>You must not call any method on that
- <code>CtClass</code> object after <code>detach()</code> is called.
-
- <p><code>ClassPool</code> objects can be cascaded like
- <code>java.lang.ClassLoader</code>. For example,
-
- <ul><pre>
- ClassPool parent = ClassPool.getDefault();
- ClassPool child = new ClassPool(parent);
- </pre></ul>
-
- <p>If <code>child.get()</code> is called, the child <code>ClassPool</code>
- first delegates to the parent <code>ClassPool</code>. If the parent
- <code>ClassPool</code> fails to find a class file, then the child
- <code>ClassPool</code> attempts to find a class file.
- If <code>child.childFirstLookup</code> is true, the child
- <code>ClassPool</code> attempts to find a class file before delegating
- to the parent <code>ClassPool</code>. For example,
-
- <ul><pre>
- ClassPool parent = ClassPool.getDefault();
- ClassPool child = new ClassPool(parent);
- child.childFirstLookup = true; // changes the behavior of the child.
- </pre></ul>
-
- <p><br>
-
- <a name="load">
- <h2>4. Class loader</h2>
-
- <p>If what classes must be modified is known in advance,
- the easiest way for modifying the classes is as follows:
-
- <ul><li>1. Get a <code>CtClass</code> object by calling
- <code>ClassPool.get()</code>,
- <li>2. Modify it, and
- <li>3. Call <code>writeFile()</code> or <code>toBytecode()</code>
- on that <code>CtClass</code> object.
- </ul>
-
- <p>If whether a class is modified or not is determined at load time,
- the users must make Javassist collaborate with a class loader.
- Javassist can be used with a class loader so that bytecode can be
- modified at load time. The users of Javassist can define their own
- version of class loader but they can also use a class loader provided
- by Javassist.
-
- <p>Using a class loader is not easy. In particular, if you are a
- beginner, you should separate your program into an application program
- and an instrumentation program. Then you should load only the former
- program by a user-defined class loader. The latter one, as well as
- the program of the user-defined class loader, should be loaded by the
- system class loader.
-
- <p><br>
-
- <h3>4.1 Class loading in Java</h3>
-
- <p>In Java, multiple class loaders can coexist and
- each class loader creates its own name space.
- Different class loaders can load different class files with the
- same class name. The loaded two classes are regarded as different
- ones. This feature enables us to run multiple application programs
- on a single JVM.
-
- <p>If the same class file is loaded by two distinct class loaders,
- the JVM makes two distinct classes with the same name and definition.
- The two classes are regarded as different ones.
- Since the two classes are not identical, an instance of one class is
- not assignable to a variable of the other class. The cast operation
- between the two classes fails and throws a <code>ClassCastException</code>.
-
- <p>Multiple class loaders form a tree structure.
- Each class loader except the bootstrap loader has a
- parent class loader, which has normally loaded the class of that child
- class loader. Since the request to load a class can be delegated along this
- hierarchy of class loaders, a class may be loaded by a class loader that
- you do not request the class loading.
- Therefore, the class loader that has been requested to load a class C
- may be different from the loader that actually loads the class C.
- For distinction, we call the former loader <em>the initiator of C</em>
- and we call the latter loader <em>the real loader of C</em>.
-
- <p>
- Furthermore, if a class loader CL requested to load a class C
- (the initiator of C) delegates
- to the parent class loader PL, then the class loader CL is never requested
- to load any classes referred to in the definition of the class C.
- CL is not the initiator of those classes.
- Instead, the parent class loader PL becomes their initiators
- and it is requested to load them.
- <em>The classes that the definition of a class C referes to are loaded by
- the real loader of C.</em>
-
- <p>To understand this behavior, let's consider the following example.
-
- <ul><pre>
- public class Point { // loaded by PL
- private int x, y;
- public int getX() { return x; }
- :
- }
-
- public class Box { // the initiator is L but the real loader is PL
- private Point upperLeft, size;
- public int getBaseX() { return upperLeft.x; }
- :
- }
-
- public class Window { // loaded by a class loader L
- private Box box;
- public int getBaseX() { return box.getBaseX(); }
- }</pre></ul>
-
- <p>Suppose that a class <code>Window</code> is loaded by a class loader L.
- Both the initiator and the real loader of <code>Window</code> are L.
- Since the definition of <code>Window</code> refers to <code>Box</code>,
- the JVM will request L to load <code>Box</code>.
- Here, suppose that L delegates this task to the parent class loader PL.
- The initiator of <code>Box</code> is L but the real loader is PL.
- In this case, the initiator of <code>Point</code> is not L but PL
- since it is the same as the real loader of <code>Box</code>.
- Thus L is never requested to load <code>Point</code>.
-
- <p>Next, let's consider a slightly modified example.
-
- <ul><pre>
- public class Point {
- private int x, y;
- public int getX() { return x; }
- :
- }
-
- public class Box { // the initiator is L but the real loader is PL
- private Point upperLeft, size;
- public Point getSize() { return size; }
- :
- }
-
- public class Window { // loaded by a class loader L
- private Box box;
- public boolean widthIs(int w) {
- Point p = box.getSize();
- return w == p.getX();
- }
- }</pre></ul>
-
- <p>Now, the definition of <code>Window</code> also refers to
- <code>Point</code>. In this case, the class loader L must
- also delegate to PL if it is requested to load <code>Point</code>.
- <em>You must avoid having two class loaders doubly load the same
- class.</em> One of the two loaders must delegate to
- the other.
-
- <p>
- If L does not delegate to PL when <code>Point</code>
- is loaded, <code>widthIs()</code> would throw a ClassCastException.
- Since the real loader of <code>Box</code> is PL,
- <code>Point</code> referred to in <code>Box</code> is also loaded by PL.
- Therefore, the resulting value of <code>getSize()</code>
- is an instance of <code>Point</code> loaded by PL
- whereas the type of the variable <code>p</code> in <code>widthIs()</code>
- is <code>Point</code> loaded by L.
- The JVM regards them as distinct types and thus it throws an exception
- because of type mismatch.
-
- <p>This behavior is somewhat inconvenient but necessary.
- If the following statement:
-
- <ul><pre>
- Point p = box.getSize();
- </pre></ul>
-
- <p>did not throw an exception,
- then the programmer of <code>Window</code> could break the encapsulation
- of <code>Point</code> objects.
- For example, the field <code>x</code>
- is private in <code>Point</code> loaded by PL.
- However, the <code>Window</code> class could
- directly access the value of <code>x</code>
- if L loads <code>Point</code> with the following definition:
-
- <ul><pre>
- public class Point {
- public int x, y; // not private
- public int getX() { return x; }
- :
- }
- </pre></ul>
-
- <p>
- For more details of class loaders in Java, the following paper would
- be helpful:
-
- <ul>Sheng Liang and Gilad Bracha,
- "Dynamic Class Loading in the Java Virtual Machine",
- <br><i>ACM OOPSLA'98</i>, pp.36-44, 1998.</ul>
-
- <p><br>
-
- <h3>4.2 Using <code>javassist.Loader</code></h3>
-
- <p>Javassist provides a class loader
- <code>javassist.Loader</code>. This class loader uses a
- <code>javassist.ClassPool</code> object for reading a class file.
-
- <p>For example, <code>javassist.Loader</code> can be used for loading
- a particular class modified with Javassist.
-
- <ul><pre>
- import javassist.*;
- import test.Rectangle;
-
- public class Main {
- public static void main(String[] args) throws Throwable {
- 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();
- :
- }
- }
- </pre></ul>
-
- <p>This program modifies a class <code>test.Rectangle</code>. The
- superclass of <code>test.Rectangle</code> is set to a
- <code>test.Point</code> class. Then this program loads the modified
- class, and creates a new instance of the
- <code>test.Rectangle</code> class.
-
- <p>If the users want to modify a class on demand when it is loaded,
- the users can add an event listener to a <code>javassist.Loader</code>.
- The added event listener is
- notified when the class loader loads a class.
- The event-listener class must implement the following interface:
-
- <ul><pre>public interface Translator {
- public void start(ClassPool pool)
- throws NotFoundException, CannotCompileException;
- public void onWrite(ClassPool pool, String classname)
- throws NotFoundException, CannotCompileException;
- }</pre></ul>
-
- <p>The method <code>start()</code> is called when this event listener
- is added to a <code>javassist.Loader</code> object by
- <code>addTranslator()</code> in <code>javassist.Loader</code>. The
- method <code>onWrite()</code> is called before
- <code>javassist.Loader</code> loads a class. <code>onWrite()</code>
- can modify the definition of the loaded class.
-
- <p>For example, the following event listener changes all classes
- to public classes just before they are loaded.
-
- <ul><pre>public class MyTranslator implements Translator {
- void start(ClassPool pool)
- throws NotFoundException, CannotCompileException {}
- void onWrite(ClassPool pool, String classname)
- throws NotFoundException, CannotCompileException
- {
- CtClass cc = pool.get(classname);
- cc.setModifiers(Modifier.PUBLIC);
- }
- }</pre></ul>
-
- <p>Note that <code>onWrite()</code> does not have to call
- <code>toBytecode()</code> or <code>writeFile()</code> since
- <code>javassist.Loader</code> calls these methods to obtain a class
- file.
-
- <p>To run an application class <code>MyApp</code> with a
- <code>MyTranslator</code> object, write a main class as following:
-
- <ul><pre>
- import javassist.*;
-
- 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);
- }
- }
- </pre></ul>
-
- <p>To run this program, do:
-
- <ul><pre>
- % java Main <i>arg1</i> <i>arg2</i>...
- </pre></ul>
-
- <p>The class <code>MyApp</code> and the other application classes
- are translated by <code>MyTranslator</code>.
-
- <p>Note that <em>application</em> classes like <code>MyApp</code> cannot
- access the <em>loader</em> classes such as <code>Main2</code>,
- <code>MyTranslator</code> and <code>ClassPool</code> because they
- are loaded by different loaders. The application classes are loaded
- by <code>javassist.Loader</code> whereas the loader classes such as
- <code>Main</code> are by the default Java class loader.
-
- <p><code>javassist.Loader</code> searches for classes in a different
- order from <code>java.lang.ClassLoader</code>.
- <code>ClassLoader</code> first delegates the loading operations to
- the parent class loader and then attempts to load the classes
- only if the parent class loader cannot find them.
- On the other hand,
- <code>javassist.Loader</code> attempts
- to load the classes before delegating to the parent class loader.
- It delegates only if:
-
- <ul><li>the classes are not found by calling <code>get()</code> on
- a <code>ClassPool</code> object, or
-
- <p><li>the classes have been specified by using
- <code>delegateLoadingOf()</code>
- to be loaded by the parent class loader.
- </ul>
-
- <p>This search order allows loading modified classes by Javassist.
- However, it delegates to the parent class loader if it fails
- to find modified classes for some reason. Once a class is loaded by
- the parent class loader, the other classes referred to in that class will be
- also loaded by the parent class loader and thus they are never modified.
- Recall that all the classes referred to in a class C are loaded by the
- real loader of C.
- <em>If your program fails to load a modified class,</em> you should
- make sure whether all the classes using that class have been loaded by
- <code>javassist.Loader</code>.
-
- <p><br>
-
- <h3>4.3 Writing a class loader</h3>
-
- <p>A simple class loader using Javassist is as follows:
-
- <ul><pre>import javassist.*;
-
- public class SampleLoader extends ClassLoader {
- /* Call MyApp.main().
- */
- public static void main(String[] args) throws Throwable {
- SampleLoader s = new SampleLoader();
- Class c = s.loadClass("MyApp");
- c.getDeclaredMethod("main", new Class[] { String[].class })
- .invoke(null, new Object[] { args });
- }
-
- private ClassPool pool;
-
- public SampleLoader() throws NotFoundException {
- pool = new ClassPool();
- pool.insertClassPath("./class"); // <em>MyApp.class must be there.</em>
- }
-
- /* Finds a specified class.
- * The bytecode for that class can be modified.
- */
- protected Class findClass(String name) throws ClassNotFoundException {
- try {
- CtClass cc = pool.get(name);
- // <em>modify the CtClass object here</em>
- byte[] b = cc.toBytecode();
- return defineClass(name, b, 0, b.length);
- } catch (NotFoundException e) {
- throw new ClassNotFoundException();
- } catch (IOException e) {
- throw new ClassNotFoundException();
- } catch (CannotCompileException e) {
- throw new ClassNotFoundException();
- }
- }
- }</pre></ul>
-
- <p>The class <code>MyApp</code> is an application program.
- To execute this program, first put the class file under the
- <code>./class</code> directory, which must <em>not</em> be included
- in the class search path. Otherwise, <code>MyApp.class</code> would
- be loaded by the default system class loader, which is the parent
- loader of <code>SampleLoader</code>.
- The directory name <code>./class</code> is specified by
- <code>insertClassPath()</code> in the constructor.
- You can choose a different name instead of <code>./class</code> if you want.
- Then do as follows:
-
- <ul><code>% java SampleLoader</code></ul>
-
- <p>The class loader loads the class <code>MyApp</code>
- (<code>./class/MyApp.class</code>) and calls
- <code>MyApp.main()</code> with the command line parameters.
-
- <p>This is the simplest way of using Javassist. However, if you write
- a more complex class loader, you may need detailed knowledge of
- Java's class loading mechanism. For example, the program above puts the
- <code>MyApp</code> class in a name space separated from the name space
- that the class <code>SampleLoader</code> belongs to because the two
- classes are loaded by different class loaders.
- Hence, the
- <code>MyApp</code> class cannot directly access the class
- <code>SampleLoader</code>.
-
- <p><br>
-
- <h3>4.4 The <code>toClass</code> method in <code>CtClass</code></h3>
-
- <p>The <code>CtClass</code> provides a convenience method
- <code>toClass</code>, which loads the class by an internal class
- loader of Javassist. This method first obtains the class file
- representing the modified class and loads it by an instance of
- <code>javassist.ClassPool.SimpleLoader</code>.
- The following code is the definition of this class loader:
-
- <ul><pre>
- public class SimpleLoader extends ClassLoader {
- public Class loadClass(String classname, byte[] classfile)
- throws ClassFormatError
- {
- Class c = defineClass(classname, classfile, 0, classfile.length);
- resolveClass(c);
- return c;
- }
- };
- </pre></ul>
-
- <p><code>loadClass()</code> loads the class specified by
- <code>classfile</code>.
- Thus, <code>toClass()</code> is equivalent to the following code:
-
- <ul><pre>
- CtClass cc = ... ;
- ClassPool.SimpleLoader cl = new ClassPool.SimpleLoader();
- Class c = cl.loadClass(cc.getName(), cc.toBytecode());
- </pre></ul>
-
- <p>Note that this class loader might be too simple for realistic use.
- It delegates to the parent class loader unless the class is explicitly
- loaded by <code>loadClass()</code> (or <code>toClass()</code> in
- <code>CtClass</code>). If you encounter an unexpected
- <code>ClassCastException</code>, you should check the class loader of
- the object. Call <code>getClass().getClassLoader()</code> on the
- object and make sure that the destination class and the source class
- of the cast operation have been loaded by the same class loader.
-
- <p><br>
-
- <h3>4.5 Modifying a system class</h3>
-
- <p>The system classes like <code>java.lang.String</code> cannot be
- loaded by a class loader other than the system class loader.
- Therefore, <code>SampleLoader</code> or <code>javassist.Loader</code>
- shown above cannot modify the system classes at loading time.
-
- <p>If your application needs to do that, the system classes must be
- <em>statically</em> modified. For example, the following program
- adds a new field <code>hiddenValue</code> to <code>java.lang.String</code>:
-
- <ul><pre>ClassPool pool = ClassPool.getDefault();
- CtClass cc = pool.get("java.lang.String");
- cc.addField(new CtField(CtClass.intType, "hiddenValue", cc));
- pool.writeFile("java.lang.String", ".");</pre></ul>
-
- <p>This program produces a file <code>"./java/lang/String.class"</code>.
-
- <p>To run your program <code>MyApp</code>
- with this modified <code>String</code> class, do as follows:
-
- <ul><pre>
- % java -Xbootclasspath/p:. MyApp <i>arg1</i> <i>arg2</i>...
- </pre></ul>
-
- <p>Suppose that the definition of <code>MyApp</code> is as follows:
-
- <ul><pre>public class MyApp {
- public static void main(String[] args) throws Exception {
- System.out.println(String.class.getField("hiddenValue").getName());
- }
- }</pre></ul>
-
- <p>If the modified <code>String</code> class is correctly loaded,
- <code>MyApp</code> prints <code>hiddenValue</code>.
-
- <p><i>Note: Applications that use this technique for the purpose of
- overriding a system class in <code>rt.jar</code> should not be
- deployed as doing so would contravene the Java 2 Runtime Environment
- binary code license.</i>
-
- <p><br>
-
- <a href="tutorial2.html">Next page</a>
-
- <hr>
- Java(TM) is a trademark of Sun Microsystems, Inc.<br>
- Copyright (C) 2000-2004 by Shigeru Chiba, All rights reserved.
- </body>
- </html>
|