Java Classloader

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、




你可能感兴趣的:(Java Classloader)