为什么要使用自定义ClassLoader很多时候人们会选择使用自定义的ClassLoader,而不使用系统的ClassLoader。这样做的原因是,在编译时无法预知运行时会需要哪些Class,特别是在一些AppServer中,比如Tomcat、Avalon-Phonix、Jboss;或是程序提供某种插件(Plug-In)的特性,让用户可以在只拥有程序二进制代码的情况下添加自己的功能,比如Ant、
Jxta-shell等。
ClassLoader内部结构通常定制一个ClassLoader很简单,一般只需要很少的几个步骤就可以完成。
Java规范规定,所有的用户自定义ClassLoader都必须从抽象类“java.lang.ClassLoader”类继承而来。下面先看一下这个类的内部实现,以帮助我们更好的理解相关内容。
protected synchronized Class< > loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
通常我们使用ClassLoader.loadClass(String
name):Class根据指定的类名得到一个相应的Class实例。从Java源代码中我们可以看到,缺省的ClassLoader做了如下的工作: 1. 调用FindLoadedClass(String):Class 检查一下这个Class是否已经被加载过了。由于JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,因此如果一个Class已经被加载过,直接从缓存中获取即可。 2. 调用它的父类的LoadClass()方法,如果它的父类不为空,则使用JVM内部的ClassLoader(即著名的BootstrapClassloader)来加载这个Class。在第10行我们可以看到使用了一个Native方法来调用这个Bootstrap
classloader。 3. 如果上面两步都没有找到,调用findClass(String):Class方法来查找并加载这个Class。 因此我们只要覆盖这个findClass(String):Class方法即可达到定义ClassLoader的要求。
1.FileClassLoader.java
package jack.classloader.own.local;
//Load local class file
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class FileClassLoader extends ClassLoader {
public Class< > findClass(String name){
byte [] data = loadClassData(name);
return defineClass(name, data, 0, data.length);
}
private byte[] loadClassData(String name){
FileInputStream fis = null;
byte [] data = null;
try {
fis = new FileInputStream(
new File(getFinalPath(name)));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = fis.read()) != -1) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
private String getFinalPath(String name){
String finalPath = "";
String path = System.getProperty("user.dir");
String [] nameStrings = name.split("\\.");
String tempStr = "";
for(String str : nameStrings){
tempStr = tempStr + str + File.separatorChar;
}
tempStr = tempStr.substring(0, tempStr.lastIndexOf(File.separatorChar));
finalPath = path + File.separator + "bin" + File.separator + tempStr + ".class";
return finalPath;
}
}
2.RemoteClassLoader.java
package jack.classloader.own.remote;
//Looa remote class file
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class RemoteClassLoader extends ClassLoader {
public Class< > findClass(String name){
byte [] data = loadClassData(name);