Sponsored Link •
|
Advertisement
|
The class loader described in the previous section, which was originally designed for a 1.1 virtual
machine, will still work in 1.2. Although 1.2 added a concrete default implementation of
loadClass()
to java.lang.ClassLoader
, this concrete
method can still be overridden in subclasses. Because the contract of loadClass()
did not change from 1.1 to 1.2, legacy user-defined class loaders that override
loadClass()
should still work as expected in 1.2.
The basic contract of loadClass()
is this: Given the fully qualified name of the
type to find, the loadClass()
method should in some way attempt to locate or
produce an array of bytes, purportedly in the Java class file format, that define the type. If
loadClass()
is unable to locate or produce the bytes, it should throw
ClassNotFoundException
. Otherwise, loadClass()
should pass the array of bytes to one of the defineClass()
methods declared in
class ClassLoader
. By passing the byte array to
defineClass()
, loadClass()
asks the virtual machine to
import the type represented by the passed byte array into the namespace of this user-defined class loader.
When loadClass()
calls defineClass()
in 1.2, it can also
specify a protection domain with which the type data should be associated. When the
loadClass()
method of a class loader successfully loads a type, it returns a
java.lang.Class
object to represent the newly loaded type.
The concrete implementation of loadClass()
from class
java.lang.ClassLoader
fullfills the loadClass()
method's contract using these four basic steps:
findLoadedClass()
). If so, return the Class
instance for that
already-loaded type.
Class
instance, return that same Class
instance.
findClass()
, which should attempt to locate or produce an
array of bytes, purportedly in the Java class file format, that define the desired type. If successful,
findClass()
should pass those bytes to defineClass()
,
which will attempt to import the type and return a Class
instance. If
findClass()
returns a Class
instance,
loadClass()
returns that same Class
instance.
findClass()
completes abruptly with some exception, and
loadClass()
completes abruptly with the same exception.
Although in 1.2 you can still subclass ClassLoader
and override the
loadClass()
method, the recommended approach to creating your own user-defined
class loader in 1.2 is to subclass ClassLoader
and implement the
findClass()
method. The findClass()
method looks like
this:
// A method declared in class java.lang.ClassLoader: protected Class findClass(String name) throws ClassNotFoundException;
The basic contract of the findClass()
method is this:
findClass()
accepts the fully qualified name of a desired type as its only parameter.
findClass()
first attempts to locate or produce an array of bytes, purportedly in the
Java class file format, that define the type of the requested name. If findClass()
is
unable to locate or produce the array of bytes, it completes abruptly with
ClassNotFoundException
. Otherwise, findClass()
invokes defineClass()
, passing in the requested name, the array of bytes and,
optionally, a ProtectionDomain
object with which the type should be associated. If
defineClass()
returns a Class
instance for the type,
findClass()
simply returns that same Class
instance to its
caller. Otherwise, defineClass()
completes abruptly with some exception, and
findClass()
completes abruptly with the same exception.
Here's a version of GreeterClassLoader
that, rather than overriding
loadClass()
, merely overrides findClass()
:
// On CD-ROM in file // linking/ex7/com/artima/greeter/GreeterClassLoader.java package com.artima.greeter; import java.io.*; public class GreeterClassLoader extends ClassLoader { // basePath gives the path to which this class // loader appends "/.class" to get the // full path name of the class file to load private String basePath; public GreeterClassLoader(String basePath) { this.basePath = basePath; } public GreeterClassLoader(ClassLoader parent, String basePath) { super(parent); this.basePath = basePath; } protected Class findClass(String className) throws ClassNotFoundException { byte classData[]; // Try to load it from the basePath directory. classData = getTypeFromBasePath(className); if (classData == null) { throw new ClassNotFoundException(); } // Parse it return defineClass(className, classData, 0, classData.length); } private byte[] getTypeFromBasePath(String typeName) { FileInputStream fis; String fileName = basePath + File.separatorChar + typeName.replace('.', File.separatorChar) + ".class"; try { fis = new FileInputStream(fileName); } catch (FileNotFoundException e) { return null; } BufferedInputStream bis = new BufferedInputStream(fis); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { int c = bis.read(); while (c != -1) { out.write(c); c = bis.read(); } } catch (IOException e) { return null; } return out.toByteArray(); } }
This version of GreeterClassLoader
appears in the
linking/ex7
directory of the CD-ROM. All of the source files in
linking/ex6
, which were described in detail in previous sections, appear unchanged
in linking/ex7
, except for GreeterClassLoader.java
.
Where the GreeterClassLoader
class, described in the previous section, that
overrides loadClass()
appears in linking/ex6
, the
GreeterClassLoader
, described in this section, that overrides
findClass()
appears in linking/ex7
.
This second version of GreeterClassLoader
declares one instance variable,
basePath
, which is a String
that is used to store the directory
path in which findClass()
should look for the class file of the type it has been
requested to load. The basePath
String
is the only parameter
passed to GreetingClassLoader
's 1-arg constructor. Because the 1-arg
constructor accepts no reference to a caller-specified parent class loader, this class loader can't invoke the
superclass constructor that takes a reference to a user-defined class loader. Thus, it simply invokes the
superclass's no-arg constructor by default, which sets this class loader's parent to be the system class loader.
The other constructor (the 2-arg constructor), however, accepts a reference to a user-defined class loader
instance as well as the basePath
String
. This constructor explicitly
invokes the superclass's 1-arg constructor, passing along the reference. The superclass sets this class loader's
parent to be the passed user-defined class loader instance.
By comparing the implementation of findClass()
in this version of
GreeterClassLoader
with the implementation of
loadClass()
in the previous version of
GreeterClassLoader
, you can easily see how much simpler it is to write
findClass()
than loadClass()
. You have much less to
worry about when you write findClass()
, and fewer opportunities to make
mistakes. findClass()
merely invokes
getTypeFromBasePath()
to attempt in this user-defined class loader's custom way
to load the requested type. If getTypeFromBasePath()
is unable to locate the
requested type in the basePath
directory, it returns null
and
findClass()
throws a ClassNotFoundException
.
Otherwise, getTypeFromBasePath()
returns the array of bytes, which
findClass()
simply passes on to defineClass()
. If
defineClass()
returns a reference to a Class
instance to
represent the successfully loaded type, findClass()
returns that same reference.
Otherwise, defineClass()
completes abruptly with an exception, which causes
findClass()
to complete abruptly with the same exception.
The findClass()
method's contract is a subset of the
loadClass()
method's contract. findClass()
isolates the
only two parts of loadClass()
that should in general be customized by subclasses of
java.lang.ClassLoader
:
When an implementation of findClass()
performs these two tasks, the result is
an array of bytes and a reference to a ProtectionDomain
object.
findClass()
passes both the byte array and the
ProtectionDomain
reference to defineClass()
.
Sponsored Links
|