ClassLoader 負責加載來自文件系統,網絡系統或其餘來源的類文件,JAVA虛擬機中的類加載器使用的是雙親委派模式。java
JAVA 虛擬機中的類加載器默認使用的是雙親委派模式,其中有三種默認的類加載器,BootStrapClassLoader,ExtensionClassLoader,SystemClassLoader(AppClassLoader),每種類加載器都已經肯定從哪一個位置加載類文件。apache
根據雙親委派模式,在加載類文件時,子加載器會首先將加載請求委託給它的父加載器,父加載器會檢測本身是否已經加載過該類,若是以加載,則加載過程結束,若是未加載則請求向上傳遞,直到BootStrap ClassLoader。若是始終未檢測到該類被加載,則從BootStrap ClassLoader 開始嘗試從其對應的路勁中加載該類文件,若是加載失敗,則有子類加載器嘗試加載,直至發起請求的子加載器爲止。數組
雙親委派模式能夠保證兩點tomcat
一 子加載器能夠使用父加載器已加載的類,但父加載器不能使用子加載器加載的類。安全
二 父加載器已加載過的類,沒法被子類再次加載,這樣就能夠保證JVM的安全性和穩定性。網絡
在不少場景中,系統會使用不一樣的類加載器完成不一樣的任務,這裏以tomcat爲例簡單介紹一下。app
Tomcat 會爲每一個部署的應用建立一個惟一的類加載,也就是WebApp ClassLoader,它負責加載該應用的this
WEB-INF/lib 目錄下的Jar文件以及WEB-INF/classes 目錄下的Class 文件。因爲每一個應用都有本身的WebAppClassLoader。WebAppClassLoader 的父加載器是Common ClassLoader ,因此不一樣的應用能夠使用Common ClassLoader 加載的共享類庫。url
MyBatis 的IO包中 提供的ClassLoaderWrapper 是一個ClassLoader 的包裝器,其中包含了多個ClassLoader 對象,經過調整多個類加載器的使用順序,ClassLoaderWrapper 能夠確保返回給系統使用的是正確的類加載器。ClassLoaderWrapper 會按照指定的順序依次檢測其中封裝的ClassLoader對象,並從中選取第一個可用的ClassLoader 對象。spa
package org.apache.ibatis.io; import java.io.InputStream; import java.net.URL; /** * A class to wrap access to multiple class loaders making them work as one * * @author Clinton Begin */ public class ClassLoaderWrapper { // 默認的類加載器 ClassLoader defaultClassLoader; // System ClassLoader ClassLoader systemClassLoader; ClassLoaderWrapper() { try { // 初始化SystemClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); } catch (SecurityException ignored) { // AccessControlException on Google App Engine } } public URL getResourceAsURL(String resource) { //getClassLoaders() 返回ClassLoader[] 數組,指名了類加載器的使用順序 return getResourceAsURL(resource, getClassLoaders(null)); } public URL getResourceAsURL(String resource, ClassLoader classLoader) { return getResourceAsURL(resource, getClassLoaders(classLoader)); } public InputStream getResourceAsStream(String resource) { return getResourceAsStream(resource, getClassLoaders(null)); } public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader)); } public Class<?> classForName(String name) throws ClassNotFoundException { return classForName(name, getClassLoaders(null)); } public Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException { return classForName(name, getClassLoaders(classLoader)); } // resource(資源的地址) ,按照ClassLoader[] 加載器的順序進行加載,返回InputStream InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null; } // 從指定的資源(resource資源的URL) 按順序(ClassLoader[]中的順序)加載 // 加載到就return URL getResourceAsURL(String resource, ClassLoader[] classLoader) { URL url; //遍歷ClassLoader 數組 for (ClassLoader cl : classLoader) { if (null != cl) { url = cl.getResource(resource); if (null == url) { url = cl.getResource("/" + resource); } if (null != url) { return url; } } } // didn't find it anywhere. return null; } // Class.forName() Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException { for (ClassLoader cl : classLoader) { if (null != cl) { try { Class<?> c = Class.forName(name, true, cl); if (null != c) { return c; } } catch (ClassNotFoundException e) { // we'll ignore this until all classloaders fail to locate the class } } } throw new ClassNotFoundException("Cannot find class: " + name); } ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ //參數指定的類加載器 classLoader, // 系統指定的默認加載器 defaultClassLoader, //當前線程綁定的類加載器 Thread.currentThread().getContextClassLoader(), // 當前類使用的類加載器 getClass().getClassLoader(), // System ClassLoader(App ClassLoader) systemClassLoader}; } }