1. 程式人生 > >java ClassLoader機制和如何載入外部class檔案(含程式碼)

java ClassLoader機制和如何載入外部class檔案(含程式碼)

 生命週期有:載入(Loading)--》驗證(Verification)--》準備(Preparation)---》解析(Resolution)--》初始化(Initiation)---》使用(Using)----》解除安裝(Unloading)。 其中標黃的驗證---》準備---》解析被稱為連線(Linking)。

就程式碼執行而言:

1.連線階段:不執行程式設計師程式碼

2.載入階段:可以重寫ClassLoader來執行我們的程式碼

3.連線後階段:執行該類的程式碼

ClassLoader:

ClassLoader是為了將class檔案的byte陣列,主要的方法是findClass用於查詢類。

Java7有個迅速將File程式設計byte[]的方法:

		byte[] cLassBytes = null;
		Path path;
		try {
			path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
			cLassBytes = Files.readAllBytes(path);

每個ClassLoader物件都有一個ClassLoader型別的欄位,暫且成為parent。當呼叫本類的loadClass方法載入類時,會先呼叫parent的ClassLoader,如果他沒找到,才會呼叫本類的findClass方法去查詢類。(這樣,應用的Launcher$AppClassLoader也可以通過BootStrapClassLoader來呼叫String類了)。

查詢類的順序,與對應的ClassLoader

系統查詢類的順序是:

1).Bootstrap classes: the runtime classes in rt.jar, internationalization classes in i18n.jar, and others.

2).Installed extensions: classes in JAR files in the lib/ext directory of the JRE, and in the system-wide, platform-specific extension directory (such as /usr/jdk/packages/lib/ext

 on the Solaris™ Operating System, but note that use of this directory applies only to Java™ 6 and later).

3).The class path: classes, including classes in JAR files, on paths specified by the system property java.class.path. If a JAR file on the class path has a manifest with the Class-Path attribute, JAR files specified by the Class-Path attribute will be searched also. By default, thejava.class.path property's value is ., the current directory. You can change the value by using the -classpath or -cp command-line options, or setting the CLASSPATH environment variable. The command-line options override the setting of the CLASSPATH environment variable.

中文就是:

1)rt.jar等(這個jar是什麼呢?你用java -verbose 類名 執行下就看到了:Loaded java.io.File from C:\Program Files\Java\jre1.8.0_31\lib\rt.jar,也就是經常呼叫的String、Long等類,屬於SDK中的)(用BootStrapClassLoader載入)

2)SDK中的拓展類,包名為javax的(java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思);

(用Launcher$ExtClassLoader載入)

3)系統環境變數CLASSPATH的路徑,當前目錄(或者用命令列帶 -classpath重新)

(用Launcher$AppClassLoader載入)

可是我的類檔案在D:\MyScript\TestClassLoader.class不在上述三種。

2:解決方案

1.場景:我想載入一個d盤上的類,路徑是:D:\MyScript\TestClassLoader.class,其不在上述三種情況。

通過重寫一個ClassLoader進行載入,不過有現成的URLClassLoader我們就不要重造輪子了。

自定義ClassLoader程式碼

public class MyClassLoader extends ClassLoader{
	
	@Override
	protected Class<?> findClass(String className)
			throws ClassNotFoundException {
		byte[] cLassBytes = null;
		//Java 7有下列API
<span style="white-space:pre">		</span>Path path;
		try {
			path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
			cLassBytes = Files.readAllBytes(path);
		} catch (IOException | URISyntaxException e) {
			e.printStackTrace();
		}
		Class cLass = defineClass(cLassBytes, 0, cLassBytes.length);
		return cLass;
	}
}

使用的時候:
			Class.forName("TestClassLoader", true, new MyClassLoader());
用URLClassLoader來簡化
		  ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

		    // This URL for a directory will be searched *recursively*
		    URL classes;
			try {
				classes = new URL( "file:///D:/MyScript/" );

		    ClassLoader custom = 
		        new URLClassLoader( new URL[] { classes }, systemClassLoader );

		    // this class should be loaded from your directory
		    Class< ? > clazz = custom.loadClass( "TestClassLoader" ); 

		    Method[] methods = clazz.getDeclaredMethods();
		    for (Method method : methods) {
				System.out.println(method.getName());
			}
URLClassLoader用的URL只能接受目錄和jar包:

結尾: /代表該目錄下的來; 非/而是檔案則預設為jar包。

貼一個jar包的程式碼:

public URL findResource(String name) {
try {
File file = new File(jarFile);
String url = file.toURL().toString();
return new URL("jar:"+url+"!/"+name);
} catch (Exception e) {
return null;
}

}<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);">	</span>

另外我用反射來驗證類是否載入成功了,clazz.newInstance()也可以生成相關程式碼。