如果要使用自定義類加載器加載class文件,就需要繼承java.lang.ClassLoader類。
ClassLoader有幾個重要的方法:
protectedClassLoader(ClassLoaderparent):使用指定的、用于委托操作的父類加載器創建新的類加載器。
protectedfinalClass<?>defineClass(Stringname,byte[]b,intoff,intlen):將一個byte數組轉換為Class類的實例。
protectedClass<?>findClass(Stringname):使用指定的二進制名稱查找類。
publicClass<?>loadClass(Stringname):使用指定的二進制名稱來加載類。
protectedfinalClass<?>findLoadedClass(Stringname):如果Java虛擬機已將此加載器記錄為具有給定二進制名稱的某個類的啟動加載器,則返回該二進制名稱的類。否則,返回null。
publicfinalClassLoadergetParent():返回委托的父類加載器。
protectedfinalvoidresolveClass(Class<?>c):鏈接指定的類。
如果要遵循雙親委派模型,則重寫findClass(Stringname)方法;如果不想遵循雙親委派模型,則直接重寫loadClass(Stringname)方法。
自定義遵循雙親委派模型的類加載器
ParentsDelegateClassLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
package com.zzj.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * 雙親委派類加載器,重寫findClass(name)方法 * * @author Administrator * */ public class ParentsDelegateClassLoader extends ClassLoader { private static final String EXT = ".class" ; private String path; public ParentsDelegateClassLoader() { path = this .getResource( "" ).getPath(); } public ParentsDelegateClassLoader(String path) { this .path = path; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte [] b = null ; try { b = loadClassFile(name); } catch (IOException e) { e.printStackTrace(); } return this .defineClass(name, b, 0 , b.length); } private byte [] loadClassFile(String name) throws IOException { String classFile = getClassFile(name); System.out.println( "即將加載class文件" + classFile); ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStream input = new FileInputStream(classFile); int count; byte [] temp = new byte [ 1024 ]; while ((count = input.read(temp)) > - 1 ) { out.write(temp, 0 , count); } out.close(); input.close(); return out.toByteArray(); } private String getClassFile(String name) { String pathName = name.replace( "." , File.separator); if (path.endsWith( "/" ) || path.endsWith( "\\" )) { return path + pathName + EXT; } return path + File.separator + pathName + EXT; } } |
現在類路徑classpath下和F:\\ClassloaderTest\\bin目錄下都有一個類文件com\zzj\classloader\User.class,包名為com.zzj.classloader,使用類加載器ParentsDelegateClassLoader加載F:\\ClassloaderTest\\bin下的類。
1
2
3
4
5
6
7
8
9
10
11
|
package com.zzj.classloader; public class App { private static final String path = "F:\\ClassloaderTest\\bin" ; private static final String classname = "com.zzj.classloader.User" ; public static void main(String[] args) throws Exception { ParentsDelegateClassLoader classLoader = new ParentsDelegateClassLoader(path); Class<?> clazz = classLoader.loadClass(classname); System.out.println(clazz); System.out.println(clazz.getClassLoader()); } } |
輸出:
1
2
|
class com.zzj.classloader.User sun.misc.Launcher$AppClassLoader @19821f |
User類的加載器是系統類加載器AppClassLoader,而不是我們自己定義的類加載。實際上被加載不是F:\\ClassloaderTest\\bin下的類,而是classpath下的類。這就是雙親委派模型:當ParentsDelegateClassLoader加載器接收到加載請求后,會先委托給父類加載器,如果父類加載器加載成功,則返回一個Class對象。如果加載失敗,才會讓接收到加載請求的類加載器加載。
把classpath下的User類刪掉測試運行:
1
2
3
|
即將加載 class 文件F:\ClassloaderTest\bin\com\zzj\classloader\User. class class com.zzj.classloader.User com.zzj.classloader.ParentsDelegateClassLoader @61de33 |
此時User類的加載為ParentsDelegateClassLoader。
這一點可以從ClassLoader的源碼中得到驗證:
1
2
3
|
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false ); } |
調用了重載方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 先判斷該類是否已被當前的類加載器加載 Class c = findLoadedClass(name); if (c == null ) { try { if (parent != null ) { // 如果存在父類加載器,則委派給父類加載 c = parent.loadClass(name, false ); } else { // 如果父類加載為空,則其父類加載器為引導類加載器 c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 如果父類加載器加載失敗,則自己加載,調用的就是findClass方法! c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } |
可見,如果想要破壞雙親委派模型,可以直接重寫loadClass(Stringname)方法。
自定義不遵循雙親委派模型的類加載器
NotParentsDelegateClassLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
package com.zzj.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * 非雙親委派類加載器,重寫loadClass(name)方法 * * @author Administrator * */ public class NotParentsDelegateClassLoader extends ClassLoader { private static final String EXT = ".class" ; private String path; public NotParentsDelegateClassLoader() { path = this .getResource( "" ).getPath(); } public NotParentsDelegateClassLoader(String path) { this .path = path; } @Override public Class<?> loadClass(String name) throws ClassNotFoundException { byte [] b = null ; try { b = loadClassFile(name); } catch (FileNotFoundException e) { System.err.println( "加載器" + this .getClass().getName() + "沒有找到class文件" + name + ",將委派給父類加載器!" ); // 委派給父類加載器 return getClass().getClassLoader().loadClass(name); } catch (IOException e) { System.err.println( "加載器" + this .getClass().getName() + "加載class文件" + name + "失敗,將委派給父類加載器!" ); // 委派給父類加載器 return getClass().getClassLoader().loadClass(name); } // 檢查該類是否被當前類加載器加載過(只檢查當前類加載器,不會檢查父類加載器) Class<?> clazz = findLoadedClass(name); if (clazz != null ) { System.out.println( "類" + name + "已被加載過!" ); return clazz; } else { System.out.println( "類" + name + "尚未被加載!" ); } return this .defineClass(name, b, 0 , b.length); } private byte [] loadClassFile(String name) throws IOException, FileNotFoundException { String classFile = getClassFile(name); System.out.println( "即將加載class文件" + classFile); ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStream input = new FileInputStream(classFile); int count; byte [] temp = new byte [ 1024 ]; while ((count = input.read(temp)) > - 1 ) { out.write(temp, 0 , count); } out.close(); input.close(); return out.toByteArray(); } private String getClassFile(String name) { String pathName = name.replace( "." , File.separator); if (path.endsWith( "/" ) || path.endsWith( "\\" )) { return path + pathName + EXT; } return path + File.separator + pathName + EXT; } } |
現在類路徑classpath下有一個類文件com\zzj\classloader\User.class,包名為com.zzj.classloader,使用類加載器NotParentsDelegateClassLoader加載User類。
1
2
3
4
5
6
7
8
9
10
|
package com.zzj.classloader; public class App2 { private static final String className = "com.zzj.classloader.User" ; public static void main(String[] args) throws Exception { NotParentsDelegateClassLoader classLoader = new NotParentsDelegateClassLoader(); Class<?> clazz = classLoader.loadClass(className); System.out.println(clazz); System.out.println(clazz.getClassLoader()); } } |
輸出:
1
2
3
4
5
6
|
即將加載 class 文件/E:/Myeclipse/zzjtest/WebRoot/WEB-INF/classes/com\zzj\classloader\User. class 類com.zzj.classloader.User尚未被加載! 即將加載 class 文件/E:/Myeclipse/zzjtest/WebRoot/WEB-INF/classes/java\lang\Object. class class com.zzj.classloader.User com.zzj.classloader.NotParentsDelegateClassLoader @61de33 加載器com.zzj.classloader.NotParentsDelegateClassLoader沒有找到 class 文件java.lang.Object,將委派給父類加載器! |
此時User類的加載器是NotParentsDelegateClassLoader,沒有先委托給父類,只有加載失敗才會委托給父類加載器,正好跟雙親委派模型是反的。
當然,即使加載失敗,也可以不委托給父類加載器,而指定其他的類加載器,從而可以構建更加復雜的網狀模型的類加載機制
總結
以上就是本文關于java自定義類加載器代碼示例的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/zhangzeyuaaa/article/details/42527813