Java类装载器可分为Bootrap类装载器,扩展类装载器,应用类装载器,自定义类装载器。其中,根类装载器和扩展类装载器是不能直接控制的,应用类装载器和自定义装载器则可以用来自行装载类。
应用类装载器将类装载到应用类Classloader中,而自定义类装载器将类装载到自定义(如使用URLClassloader)中,不同的自定义Classloader装载同样的Class,具有独立的内存空间,即使是类中的静态变量,也是相互隔离,不受对方赋值操作的影响。
1、一个应用类装载器的例子:
/**
* 装载jar文件
* @param file
* @throws Exception
*/
public static void loadJar(String file) throws Exception {
JarFile jar = new JarFile(file);
Enumeration<JarEntry> entries = jar.entries();
ClassLoader cl = (ClassLoader) Thread.currentThread().getContextClassLoader();
//获取ClassLoader类的defineClass(String, byte[], int, int) 方法,对protected类型修改访问权限
Method md1 = java.lang.ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, int.class, int.class);
md1.setAccessible(true);
String name;
// 装载所有的jar文件
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
name = entry.getName();
name = name.substring(0, name.length() - 6);
name = name.replaceAll("/", ".");
InputStream is = jar.getInputStream(entry);
ByteArrayOutputStream dataOut = new ByteArrayOutputStream();
byte[] packData = new byte[2048];
int readLen = 0;
while (-1 != (readLen = is.read(packData))) {
dataOut.write(packData, 0, readLen);
}
if (dataOut.size() <= 0) {
throw new ClassNotFoundException(name);
}
byte[] classFile = dataOut.toByteArray();
System.out.println("class name:" + name);
md1.invoke(cl, name, classFile, 0, classFile.length);
}
}
}
/**
*
*/
public void loadTest(){
String file = "D:\\test2\\TestedClass.jar";
try {
ClassLoaderTest.loadJar(file);
Class<?> c = Class.forName("dictquery.Hello");
Method[] ms = c.getDeclaredMethods();
Object obj = c.newInstance();
for(int i=0;i<ms.length;i++){
Method m = ms[i];
System.out.println("Method:" + m.getName());
m.invoke(obj, new Object[]{});
}
Field[] fs = c.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
System.out.println("Field:" + f.getName() + ", Modifier:" + f.getModifiers());
if((Modifier.PUBLIC+ Modifier.STATIC) == f.getModifiers()){
System.out.println("Used public and static qualify!");
}
//改变可访问属性
f.setAccessible(true);
try {
String value = (String)f.get(obj);
System.out.println("Field " + f.getName() + " default value:" + value);
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @param args
*/
public static void main(String[] args) {
ClassLoaderTest ct = new ClassLoaderTest();
ct.loadTest();
}
该方法装载类后,类的内存空间在应用装载类中。
2、一个自定义类装载器的例子:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
该类装载器装载类后,类的运行空间在自定义Classloader中,对两个使用上述
FileSystemClassLoader
或URLClassloadeer而言,对同一类,在不同的Classloader中是相互隔离的,互不受干扰。
3、自定义类装载器测试
使用URLClassloader装载:
/**
* 类装载到自定义类装载器,分别使用不同的自定义装载器装载
*/
public void classLoadTest3c(){
try {
URL[] urls = new URL[]{ new File("D:\\Users\\workspace\\ClassLoadTest\\bin").toURI().toURL() };
ClassLoader clsLoader = new URLClassLoader(urls);
Class<?> c1 = clsLoader.loadClass("test.ClassLoaderTest");
Field f = c1.getDeclaredField("testVar");
Method m = c1.getMethod("showTestVar", null);
Object obj = c1.newInstance();
m.invoke(obj, new Object[]{});
f.set(obj, "The testVar's value is modified!");
m.invoke(obj, new Object[]{});
System.out.println("--------------------");
System.out.println(this.getClass().getClassLoader());
ClassLoader clsLoader2 = new URLClassLoader(urls);
System.out.println(clsLoader2.getSystemClassLoader());
Class<?> c2 = clsLoader2.loadClass("test.ClassLoaderTest");
Object obj2 = c2.newInstance();
Method m2 = c2.getMethod("showTestVar", null);
m2.invoke(obj2, new Object[]{});
m.invoke(obj, new Object[]{});
m2.invoke(obj2, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
ClassLoaderTest.java, 参见1.
4、在已有的自定义类装载器中装载
/**
* 类装载到自定义类装载器,共用一个自定义类装载器
*/
public void classLoadTest3d(){
try {
URL[] urls = new URL[]{ new File("D:\\Users\\workspace\\ClassLoadTest\\bin").toURI().toURL() };
ClassLoader clsLoader = new URLClassLoader(urls);
Class<?> c1 = clsLoader.loadClass("test.ClassLoaderTest");
Field f = c1.getDeclaredField("testVar");
Method m = c1.getMethod("showTestVar", null);
Object obj = c1.newInstance();
m.invoke(obj, new Object[]{});
f.set(obj, "The testVar's value is modified!");
m.invoke(obj, new Object[]{});
System.out.println("--------------------");
System.out.println(this.getClass().getClassLoader());
ClassLoader clsLoader2 = new URLClassLoader(urls, clsLoader);
System.out.println(clsLoader2.getSystemClassLoader());
Class<?> c2 = clsLoader2.loadClass("test.ClassLoaderTest");
Object obj2 = c2.newInstance();
Method m2 = c2.getMethod("showTestVar", null);
m2.invoke(obj2, new Object[]{});
m.invoke(obj, new Object[]{});
m2.invoke(obj2, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
clsLoader2使用clsLoader 的Classloader,因而,clsLoader2的类"test.ClassLoaderTest" 使用clsLoader中的"test.ClassLoaderTest"
5、注意,这些测试的类均不要放置在当前项目的类路径底下。否则因为Bootrap类装载器会自动装载,再行加载已加载的类无效。
6、