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(); } }
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、