Sponsored Link •
|
Advertisement
|
Greet
ApplicationAs an example of an application that performs dynamic extension through user-defined class loaders, consider the following class:
// On CD-ROM in file linking/ex6/Greet.java import com.artima.greeter.*; public class Greet { // Arguments to this application: // args[0] - path name of directory in which class files // for greeters are stored // args[1], args[2], ... - class names of greeters to load // and invoke the greet() method on. // // All greeters must implement the com.artima.greeter.Greeter // interface. // static public void main(String[] args) { if (args.length <= 1) { System.out.println( "Enter base path and greeter class names as args."); return; } GreeterClassLoader gcl = new GreeterClassLoader(args[0]); for (int i = 1; i < args.length; ++i) { try { // Load the greeter specified on the command line Class c = gcl.loadClass(args[i]); // Instantiate it into a greeter object Object o = c.newInstance(); // Cast the Object ref to the Greeter interface type // so greet() can be invoked on it Greeter greeter = (Greeter) o; // Greet the world in this greeter's special way greeter.greet(); } catch (Exception e) { e.printStackTrace(); } } } }
The Greet
application is a fancy incarnation of the typical "Hello, world!"
program. Greet
uses a user-defined class loader to dynamically extend itself with
classes--called "greeters"--that do the actual work of telling the world hello.
A greeter is any class that implements the com.artima.greeter.Greeter
interface:
// On CD-ROM in file linking/ex6/com/artima/greeter/Greeter.java package com.artima.greeter; public interface Greeter { void greet(); }
As you can see from the code above, the Greeter
interface declares only one
method: greet()
. When a greeter object's greet()
method is
invoked, the object should say hello to the world in its own unique way. Here are a few examples of
greeters:
// On CD-ROM in file linking/ex6/greeters/Hello.java import com.artima.greeter.Greeter; public class Hello implements Greeter { public void greet() { System.out.println("Hello, world!"); } } // On CD-ROM in file linking/ex6/greeters/Greetings.java import com.artima.greeter.Greeter; public class Greetings implements Greeter { public void greet() { System.out.println("Greetings, planet!"); } } // On CD-ROM in file linking/ex6/greeters/Salutations.java import com.artima.greeter.Greeter; public class Salutations implements Greeter { public void greet() { System.out.println("Salutations, orb!"); } } // On CD-ROM in file linking/ex6/greeters/HowDoYouDo.java import com.artima.greeter.Greeter; public class HowDoYouDo implements Greeter { public void greet() { System.out.println("How do you do, globe!"); } }Greeters can be more complex than the above four examples. Here's an example of a greeter that chooses a greeting based on the time of day:
// On CD-ROM in file linking/ex6/greeters/HiTime.java import com.artima.greeter.Greeter; import java.util.Date; public class HiTime implements Greeter { public void greet() { // Date's no-arg constructor initializes itself to the // current date and time Date date = new Date(); int hours = date.getHours(); // Some hours: midnight, 0; noon, 12; 11PM, 23; if (hours >= 4 && hours <= 11) { System.out.println("Good morning, world!"); } else if (hours >= 12 && hours <= 16) { System.out.println("Good afternoon, world!"); } else if (hours >= 17 && hours <= 21) { System.out.println("Good evening, world!"); } else { System.out.println("Good night, world!"); } } }
The Greet
application doesn't know at compile-time what greeter classes it will
load and where those classes will be stored. At run-time it takes a directory path as its first command-line
argument and greeter class names as subsequent arguments. It attempts to load the greeters using the path
name as a base directory.
For example, imagine you invoke the Greet
application with the following
command line:
java Greet greeters HelloIn this command line,
java
is the name of the Java virtual machine executable.
Greet
is the class name of the Greet
application.
greeters
is the name of a directory relative to the current directory in which the
Greet
application should look for greeters. Hello
is the name of
the greeter.
When the Greet
application is invoked with the above command line, it attempts
to load greeters/Hello.class
and invoke Hello
's
greet()
method. If the Hello.class
file is indeed sitting in a
directory named greeters
, the application will print:
Hello, world!
The Greet
application can handle more than one greeter. If you invoke it with the
following command line:
java Greet greeters Hello Greetings Salutations HowDoYouDothe
Greet
application will load each of the four greeters listed and invoke their
greet()
methods, yielding the following output:
Hello, world! Greetings, planet! Salutations, orb! How do you do, globe!
The Greet
application works by first checking to make sure there are at least two
command-line arguments: a directory path and at least one greeter class name. It then instantiates a new
GreeterClassLoader
object, which will be responsible for loading the greeters.
(The inner workings of class GreeterClassLoader
, a subclass of
java.lang.ClassLoader
, will be described later in this section.) The constructor
for GreeterClassLoader
accepts a String
that it uses as a
directory path in which to look for greeters.
After it has created the GreeterClassLoader
object, the
Greet
application invokes its loadClass()
method for each
greeter name that appears on the command line. When it invokes loadClass()
, it
passes the greeter class name, args[i]
, as the sole parameter:
// Load the greeter specified on the command line Class c = gcl.loadClass(args[i]);If the
loadClass()
method is unsuccessful, it throws an exception or error. If the
loadClass()
method is successful, it returns the Class
instance
for the newly loaded type.
Note that in addition to being loaded, the type requested of loadClass()
may
possibly be linked and initialized by the time loadClass()
returns. If the type had
been actively used prior to the loadClass()
invocation that requested the type, that
active use would have triggered its loading, linking, and initialization. Regardless, by the time the next
statement, which calls newInstance()
on a Class
reference, is
executed, the type will definitely have been initialized. If the type has not yet been initialized, calling
newInstance()
will trigger the initialization of the type (which must be a class),
because a class must be initialized before an object of that class is instantiated. So if the type hadn't been
initialized prior to the loadClass()
invocation, calling
newInstance()
will trigger the initialization.
Once loadClass()
has returned a Class
instance, the
Greet
application's main()
method instantiates a new instance of
the greeter by calling newInstance()
on the Class
instance:
// Instantiate it into a greeter object Object o = c.newInstance();When the
newInstance()
method is invoked on a Class
object, the virtual machine creates and initializes a new instance of the class represented by the
Class
object. To initialize the new instance, the virtual machine invokes its no-arg
constructor. (Note that for this statement to work without throwing an exception, the newly loaded type
must be a class, not an interface, must be accessible, must not be abstract, and must contain a no-arg
constructor that is accessible.)
The Greet
application then casts the Object
reference that
points to the greeter object to type Greeter
:
// Cast the Object ref to the Greeter interface type // so greet() can be invoked on it Greeter greeter = (Greeter) o;
Finally, armed with a Greeter
reference, the main()
method
invokes the greet()
method on the greeter object:
// Greet the world in this greeter's special way greeter.greet();
The Greet
application demonstrates the flexibility inherent in Java's linking model.
The Greet
application does not know at compile time what greeters it will be loading
and dynamically linking to at run-time. In the examples above, class Greet
invokes the
greet()
method in classes Hello
,
Greetings
, Salutations
, and
HowDoYouDo
. But if you look at Greet
's constant pool, there is
no symbolic reference to any of these classes. There is only a symbolic reference to their shared
superinterface, com.artima.greeter.Greeter
. Greeters themselves, so long as
they implement the com.artima.greeter.Greeter
interface, can be anything
and can be written and compiled anytime, even after the Greet
application itself is
compiled.
Sponsored Links
|