Java虛擬機中定義了一個類裝載器子系統和執行引擎子系統.其中類裝載器子系統主要是負責裝載類二進制文件到JVM中的,而執行引擎子系統則負責執行其中的指令.二者都是虛擬機的具體實現,執行引擎程序代碼中無法控制,相對來說類裝載器就比較靈活.JVM規範中定義虛擬機必須實現啓動類裝載器(bootstarp),但是用戶可以定義自己的類裝載器.一個程序啓動的時候有3個類加載器.
第一個是啓動類加載器,它負責加載Java的核心類.它是JVM實現的一部分,不是ClassLoader的子類.是用C代碼實現的.第二個類加載器是擴展類加載器,它負責加載JDK的擴展類,也就是目錄配置屬性.第三個是APP的類加載器,通常用ClassLoader.getSystemClassLoader()可以獲得,負責加載CLASSPATH下的類.一般這3個類加載器足以滿足我們的應用.雖然假如我們的程序需要加載上述3個加載器不能到達類.那麼我們就只能定義自己的類加載器.
- public static void main(String[] args) throws Exception
- {
- try
- {
- Class.forName("org.apache.commons.lang.StringUtils");
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- File file = new File("D:"+File.separator+"commons-lang-2.5.jar");
- URL url = file.toURI().toURL();
- URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
- Class clazz = urlClassLoader.loadClass("org.apache.commons.lang.StringUtils");
- Method isEmpty = clazz.getMethod("isEmpty", String.class);
- System.out.println(isEmpty.invoke(null,"This is not empty!"));
- System.out.println(isEmpty.invoke(null,""));
- }
假設在D盤放置了一個apache的commons-lang包,想調用其中的方法,但是我們沒有把他引入到classpath.所以在上述的3個ClassLoader中根本就找不到該類的存在.如上述代碼用Class.forName方法加載類,此時使用的是AppClassLoader,它的上層ClassLoader也沒有該類的引用.所以自然會報出java.lang.ClassNotFoundException的異常出來.所以我們必須定義自己的ClassLoader去加載它,這時候URLClassLoader 就派上用場了.它接受一個URL數組爲參數,代表的意義是它可以在提供的路徑中加載到提供的類.於是上述代碼就可以使用loadClass來加載返回一個Class對象.然後用java反射來調用加載的類的方法.上述代碼是調用了org.apache.commons.lang.StringUtils的靜態方法isEmpty.
在WEB容器的實現中也用了此方法.有興趣的話可以參考下Tomcat的源碼.容器啓動的時候Bootstrap在設置完基本的目錄之後,第一件事就是先調用initClassLoaders來構造自己的類加載器層次.