Java Classloader

8802 ワード

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の中で、2つに対して上記を使用する
FileSystemClassLoader 
またはURLClassloaderでは、同じクラスに対して、異なる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();
		}
	}	
clsLoader 2はclsLoaderのClassloaderを使用するため、clsLoader 2のクラス「test.ClassLoader Test」はclsLoaderの「test.ClassLoader Test」を使用する
5、これらのテストのクラスは、現在のプロジェクトのクラスパスの下に置かないでください.それ以外の場合、Bootrapクラスマウンタは自動的にマウントされるため、ロードされたクラスを再ロードするのは無効です.
6、