何爲類加載器?
網上說:加載類的工具。我以爲做爲編程人員,把他定義成可以加載類的一段代碼更加可以去除概念的晦澀。
何爲類加載器加載了類?
一個類被類加載器加載了的意思就是經過類加載器把類的二進制加載到內存中.也就是說在類加載器加載某個類以前。這個類已是.class文件了,編譯器作了這件事情。
什麼樣的類能夠被加載?
1):本地編譯好的class中直接加載
2):網絡加載:java.net.URLClassLoader能夠加載url指定的類
3):從jar、zip等等壓縮文件加載類,自動解析jar文件找到class文件去加載util類
4):從java源代碼文件動態編譯成爲class文件
類加載過程當中都發生了什麼?
編譯器將java代碼編譯成了.class文件這只是萬里長城走完了第一步。虛擬機是如何掌握這個.class文件的操縱權的呢,這個時候就須要參謀長類加載器去作這件事情。虛擬機的類加載機制以下:
一、裝載(Load)
查找並加載類的二進制數據;
二、連接(Link)
2.一、校驗(Verify):確保被加載類的正確性;
2.二、準備(Prepare):爲類的靜態變量分配內存,並將其初始化爲默認值;
2.三、解析(Resolve):把類中的符號引用轉換爲直接引用;
三、初始化(Initialize):爲類的靜態變量賦予正確的初始值;java
從這個類加載過程來看,「參謀長」要作的事情卻是真多,也看出來「參謀長」的能力有多強。接下來咱們就來仔細看看咱們的「參謀長」。
首先咱們看看「參謀長」的長相和「參謀長」的脾氣。
jvm的類加載器的結構如圖所示:web
(1) Bootstrap ClassLoader : 將 存放於<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,而且是虛擬機識別的(僅按照文 件名識別,如 rt.jar 名字不符合的類庫即便放在lib目錄中也不會被加載)類庫加載到虛擬機內存中。啓動類加載器沒法被Java程序直接引用
(2) Extension ClassLoader : 將<JAVA_HOME>\lib\ext目錄下的,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫加載。開發者能夠直接使用擴展類加載器。
(3) Application ClassLoader : 負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者可直接使用。
(4)自定義類加載器:
屬於應用程序根據自身須要自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現ClassLoader。
「參謀長」對待須要加載的類遵照雙親委派模型的原則。 雙親委派模型的工做過程:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的 類加載器都是如此,所以全部的加載請求最終都應該傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求(它的搜索範圍中沒有找到所需 的類)時,子加載器纔會本身去加載。
以上都是JVM類加載器的知識點,分水嶺了哈,來講說servlet服務器tomcat的類加載器。
再說tomcat(7)服務器的類加載器。tomcat服務器畢竟是要符合本身的業務須要因此在原則不變的基礎上同jvm加載器是有必定的差別。做爲優秀的java web服務器他須要解決本身的問題:
① 同一個Web服務器裏,各個Web項目之間各自使用的Java類庫要互相隔離。
② 同一個Web服務器裏,各個Web項目之間能夠提供共享的Java類庫。
③ 服務器爲了避免受Web項目的影響,應該使服務器的類庫與應用程序的類庫互相獨立。
④ 對於支持JSP的Web服務器,應該支持熱插拔(hotswap)功能。
以上的東西太多太複雜,本文就介紹簡單的加載器。(代碼針對tomcat4)。
tomcat類加載器如圖所示:編程
簡單的tomcat類加載器須要知足如下需求:緩存
· 要制定類加載器的某些特定規則 tomcat
· 緩存之前加載的類 ·安全
事先加載類以預備使用服務器
public interface Loader { //返回容器使用的java類加載器 public ClassLoader getClassLoader(); //返回關聯加載器的容器 public Container getContainer(); //設置和類加載器相關聯的容器 public void setContainer(Container container); //返回和管理器相關聯的DefaultContext public DefaultContext getDefaultContext(); //設置和管理器相關的DefaultContext /** public void setDefaultContext(DefaultContext defaultContext); //返回配置類加載器的代理模式flag值 public boolean getDelegate(); //設置類加載器的代理模式flag public void setDelegate(boolean delegate); //返回類加載器的版本號、描述信息 public String getInfo(); //是否能夠自動重載 public boolean getReloadable(); //設置自動重載標示 public void setReloadable(boolean reloadable); // --------------------------------------------------------- 公共方法 //添加該容器的屬性變化監聽器 public void addPropertyChangeListener(PropertyChangeListener listener); //添加類加載器的加載位置 public void addRepository(String repository); //返回類加載器的庫加載路徑組合 public String[] findRepositories(); //類加載器是否被修改標示 public boolean modified(); //刪除該容器的屬性變化監聽器 public void removePropertyChangeListener(PropertyChangeListener listener); } --------------------- public interface Reloader { //添加新的類加載路徑 public void addRepository(String repository); //返回全部的類庫加載路徑 public String[] findRepositories(); //類加載器是否被修改了 public boolean modified(); } --------------------------------- //該類加載器用於從指向 JAR 文件和目錄的 URL 的搜索路徑加載類和資源。這裏假定任何以 '/' 結束的 URL //都是指向目錄的。若是不是以該字符結束,則認爲該 URL 指向一個將根據須要打開的 JAR 文件。 //建立 URLClassLoader 實例的 AccessControlContext 線程將在後續加載類和資源時使用。 //爲加載的類默認授予只能訪問 URLClassLoader 建立時指定的 URL 的權限。 public class URLClassLoader extends SecureClassLoader implements Closeable { //加載類和資源的url路徑 private final URLClassPath ucp; //加載類和資源的上下文 private final AccessControlContext acc; //爲給定的 URL 構造新 URLClassLoader。首先在指定的父類加載器中搜索 //URL,而後按照爲類和資源指定的順序搜索 URL。這裏假定任何以 '/' 結束的 URL //都是指向目錄的。若是不是以該字符結束,則認爲該 URL 指向一個將根據須要下載和打開的 JAR //文件。 若是有安全管理器,該方法首先調用安全管理器的 checkCreateClassLoader //方法以確保容許建立類加載器。 public URLClassLoader(URL[] urls, ClassLoader parent) { super(parent); // this is to make the stack depth consistent with 1.1 SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } ucp = new URLClassPath(urls); this.acc = AccessController.getContext(); } URLClassLoader(URL[] urls, ClassLoader parent, AccessControlContext acc) { super(parent); // this is to make the stack depth consistent with 1.1 SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } ucp = new URLClassPath(urls); this.acc = acc; } /** 使用默認的委託父 ClassLoader 爲指定的 URL 構造一個新 URLClassLoader。首先在父類加載器中搜索 URL,而後按照爲類和資源指定的順序搜索 URL。這裏假定任何以 '/' 結束的 URL 都是指向目錄的。若是不是以該字符結束,則認爲該 URL 指向一個將根據須要下載和打開的 JAR 文件。 若是有安全管理器,該方法首先調用安全管理器的 checkCreateClassLoader 方法以確保容許建立類加載器。 */ public URLClassLoader(URL[] urls) { super(); // this is to make the stack depth consistent with 1.1 SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkCreateClassLoader(); } ucp = new URLClassPath(urls); this.acc = AccessController.getContext(); } //回給定 codesource 對象的權限。該方法的實現首先調用 super.getPermissions,而後基於 //codesource 的 URL 添加權限。 //若是此 URL 的協議爲 "jar",那麼授予的權限將基於 Jar 文件 URL 所請求的權限。 //若是協議爲 "file",而且路徑指定了某個文件,則要授予對該文件的讀權限。若是協議爲 //"file",而且路徑是一個目錄,則要授予該目錄中的全部文件及其(遞歸)子目錄中包含的全部文件讀權限。 protected PermissionCollection getPermissions(CodeSource codesource) { PermissionCollection perms = super.getPermissions(codesource); URL url = codesource.getLocation(); Permission p; URLConnection urlConnection; try { urlConnection = url.openConnection(); p = urlConnection.getPermission(); } catch (java.io.IOException ioe) { p = null; urlConnection = null; } if (p instanceof FilePermission) { // if the permission has a separator char on the end, // it means the codebase is a directory, and we need // to add an additional permission to read recursively String path = p.getName(); if (path.endsWith(File.separator)) { path += "-"; p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); } } else if ((p == null) && (url.getProtocol().equals("file"))) { String path = url.getFile().replace('/', File.separatorChar); path = ParseUtil.decode(path); if (path.endsWith(File.separator)) path += "-"; p = new FilePermission(path, SecurityConstants.FILE_READ_ACTION); } else { /** * Not loading from a 'file:' URL so we want to give the class * permission to connect to and accept from the remote host * after we've made sure the host is the correct one and is valid. */ URL locUrl = url; if (urlConnection instanceof JarURLConnection) { locUrl = ((JarURLConnection)urlConnection).getJarFileURL(); } String host = locUrl.getHost(); if (host != null && (host.length() > 0)) p = new SocketPermission(host, SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION); } // make sure the person that created this class loader // would have this permission if (p != null) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { final Permission fp = p; AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() throws SecurityException { sm.checkPermission(fp); return null; } }, acc); } perms.add(p); } return perms; } //爲指定的 URL 和父類加載器建立新 URLClassLoader 實例。若是安裝了安全管理器,該方法返回的 //URLClassLoader 的 loadClass 方法將在加載該類以前調用 SecurityManager.checkPackageAccess //方法。 public static URLClassLoader newInstance(final URL[] urls, final ClassLoader parent) { // Save the caller's context final AccessControlContext acc = AccessController.getContext(); // Need a privileged block to create the class loader URLClassLoader ucl = AccessController.doPrivileged( new PrivilegedAction<URLClassLoader>() { public URLClassLoader run() { return new FactoryURLClassLoader(urls, parent, acc); } }); return ucl; } //加載器工廠類 final class FactoryURLClassLoader extends URLClassLoader { static { ClassLoader.registerAsParallelCapable(); } FactoryURLClassLoader(URL[] urls, ClassLoader parent, AccessControlContext acc) { super(urls, parent, acc); } FactoryURLClassLoader(URL[] urls, AccessControlContext acc) { super(urls, acc); } public final Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // First check if we have permission to access the package. This // should go away once we've added support for exported packages. SecurityManager sm = System.getSecurityManager(); if (sm != null) { int i = name.lastIndexOf('.'); if (i != -1) { sm.checkPackageAccess(name.substring(0, i)); } } return super.loadClass(name, resolve); } 其餘略 }