類加載器--3
從JVM加載類的過程再看類加載器
從Java源碼再看雙親委派模型
----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------
1. 從JVM加載類的過程再看類加載器
(1). 類加載過程的加載階段要點回顧
[1]. 在類加載過程的加載階段中,JVM的規範之一是“通過全類名獲取定義此類的二進制字節(byte)流”。
[2]. 但是JVM並沒有對這條規範中“從哪裏獲取此類的二進制流”並且“怎樣獲取此類的二進制流”做出明確的規定。
[3]. 進一步說:JVM設計團隊將“通過全類名獲取定義此類的二進制字節(byte)流”這個動作放到JVM的外部去實現,以便讓應用程序自己決定如何去獲取所需要的類。
(2). 通過加載階段認識類加載器
再次理解類加載器:實現“通過全類名獲取定義此類的二進制字節(byte)流”這個動作代碼的模塊稱爲類加載器
2. 從Java源碼再看雙親委派模型
1). ClassLoader抽象類中loadClass的源碼
(1). 供JVM直接調用的是public訪問權限的loadClass()方法
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
在這個方法中,又一次調用了訪問權限是protected的重載的loadClass方法。這個方法體現了類加載器的雙親委託模型
(2). 雙親委託機制的源碼體現
[1]. 訪問權限爲protected的loadClass方法的調用者
默認的由JVM調用的public的loadClass調用這個方法在protected的loadClass的第二個參數的boolean resolve的位置傳入的參數是false
[2]. loadClass源碼的流程
【說明】代碼中使用了含有緩存cache機制的雙親委託機制。首先在緩存cache中查找是否存在這個Class對象,如果存在就直接返回。否則再去使用雙親委託機制去加載這個Class對象。
step1. 檢測此Class是否載被加載過( 即在cache中是否有此Class )
{1}. 如果已經被加載過並找到,去step7直接返回這個被加載過的Class對象
{2}. 如果沒有,跳轉到step2
step2. 檢查這個類加載器的父classloader是否存在
{1}. 如果當前類加載器的父類加載器仍然存在,將調用父類類加載器的loadClass()方法對指定的類進行加載。
{2}. 如果當前類加載器的父類加載器不存在(沒有parent,那parent一定是Bootstrap classloader ),跳轉到step4
step3. 請求父類加載器對指定的類進行載入
{1}. 如果成功到step7
{2}. 不成功到step5
step4. 請求JVM從Bootstrap 類加載器對相應的類進行加載
{1}. 如果成功,跳轉到step 7
{2}. 如果不成功,執行step5。
step5. 調用類加載器findClass( )方法對指定的類進行加載 (從與此classloader相關的類路徑中尋找)
{1}. 如果找到則執行step7
{2}. 否則執行step6
step6. 拋出ClassNotFoundException.
step7. 返回Class.
[3]. loadClass源碼
2). ExtClassLoader、AppClassLoader的真實繼承體系
(1). SecureClassLoader類
[1]. 所處位置:java.security包
[2]. 源碼聲明
public class SecureClassLoader extends ClassLoader {…}
[3]. 特點
{1}. 直接繼承了抽象類java.lang.ClassLoader
{2}. 這個類對java.lang.ClassLoader中的defineClass方法提供了重載的方法,增加了如下兩個重載方法。【注意這兩個重載的方法是final的,也就是不能被重寫】
{2}1. protected final Class<?> defineClass(String name, byte[] b,int off, int len, CodeSource cs)
{2}2. protected final Class<?> defineClass(String name, java.nio.ByteBufferb, CodeSource cs)
【注意】這兩個重載的方法內部實現都是對java.lang.ClassLoader的defineClass( )方法的簡單封裝。
{3}. 增加對系統默認策略檢索的權限的支持【瞭解即可】
(2). URLClassLoader類
[1]. 所處位置:java.net包
[2]. 源碼聲明
public class URLClassLoader extendsSecureClassLoader implementsCloseable {
//字節碼的檢索路徑
private final URLClassPath ucp;
//…
//重寫了java.lang.ClassLoader類的findClass方法
protected Class<?> findClass(final String name) throws ClassNotFoundException{//…}
//…
}
[3]. URLClassLoader作用
{1}. 這個類可以從指定的搜索路徑加載字節碼文件或者資源文件。
{2}. 指定的路徑可以是對JAR文件的引用,也可以是對普通路徑的引用
[4]. 特點
{1}. 直接繼承了類java.security.SecureClassLoader
{2}. 重載了defineClass( )方法, private訪問屬性
{3}. 重寫了父類的findClass( )方法
[5]. URLClassLoader重寫的父類defineClass( )源碼
{1}. 作用
從指定的資源中獲取Class類對象
{2}. 源碼
private Class defineClass(String name, Resource res)throws IOException {
long t0 = System.nanoTime();
int i = name.lastIndexOf('.');
URLurl = res.getCodeSourceURL();
if (i != -1) {
String pkgname = name.substring(0, i);
// Check ifpackage already loaded.
Manifest man = res.getManifest();
if (getAndVerifyPackage(pkgname, man,url) == null) {
try {
if (man != null) {
definePackage(pkgname, man,url);
} else {
definePackage(pkgname, null, null, null, null, null, null, null);
}
} catch (IllegalArgumentException iae) {
// parallel-capable class loaders:re-verify in case of a
// race condition
if (getAndVerifyPackage(pkgname, man, url) == null) {
// Should never happen
throw new AssertionError("Cannot find package " +
pkgname);
}
}
}
}
// Now read the class bytes anddefine the class
java.nio.ByteBuffer bb = res.getByteBuffer();
if (bb != null) {
// Use(direct) ByteBuffer:
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = newCodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// must readcertificates AFTER reading bytes.
CodeSigner[] signers =res.getCodeSigners();
CodeSource cs = newCodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, b, 0, b.length,cs);
}
}
[6]. URLClassLoader重寫的父類findClass( )源碼
{1}. 作用
根據指定的類名從URL路徑中尋找並加載指定的Class。其中指定的URL路徑被URLClassLoader的成員變量ucp所記錄。
{2}. 源碼
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
try {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Class>() {
public Class run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res =ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
throw new ClassNotFoundException(name);
}
}
}, acc);
} catch (java.security.PrivilegedActionExceptionpae) {
throw (ClassNotFoundException)pae.getException();
}
}
【結論】可以看到,findClass方法就是通過拼接路徑字符串來獲取最終的class文件所在的路徑,並進行加載 String path = name.replace('.', '/').concat(".class");
(3). Launcher$AppClassLoader類
[1]. 位置:sun.misc.Launcher的內部
[2]. AppClassLoader源碼 (從Launcher內部摘取)
由於AppClassLoader是靜態內部類,依附於Launcher這個外部類,以下是源碼。
static class AppClassLoader extendsURLClassLoader{
public static ClassLoader getAppClassLoader(ClassLoader paramClassLoader)
throws IOException{
String str = System.getProperty("java.class.path");
File[] arrayOfFile = str == null ? new File[0] : Launcher.access$200(str);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction(str, arrayOfFile,paramClassLoader)
{
public Launcher.AppClassLoader run() {
URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.access$300(this.val$path);
return new Launcher.AppClassLoader(arrayOfURL, this.val$extcl);
}
});
}
AppClassLoader(URL[] paramArrayOfURL, ClassLoader paramClassLoader){
super(paramClassLoader, Launcher.factory);
}
public Class loadClass(String paramString, boolean paramBoolean) throws ClassNotFoundException{
int i = paramString.lastIndexOf('.');
if (i != -1) {
SecurityManager localSecurityManager = System.getSecurityManager();
if (localSecurityManager != null) {
localSecurityManager.checkPackageAccess(paramString.substring(0, i));
}
}
return super.loadClass(paramString, paramBoolean);
}
protected PermissionCollection getPermissions(CodeSourceparamCodeSource){
PermissionCollection localPermissionCollection = super.getPermissions(paramCodeSource);
localPermissionCollection.add(new RuntimePermission("exitVM"));
return localPermissionCollection;
}
private void appendToClassPathForInstrumentation(StringparamString){
assert (Thread.holdsLock(this));
super.addURL(Launcher.getFileURL(new File(paramString)));
}
private static AccessControlContext getContext(File[]paramArrayOfFile)throws MalformedURLException{
PathPermissions localPathPermissions = new PathPermissions(paramArrayOfFile);
ProtectionDomain localProtectionDomain = new ProtectionDomain(newCodeSource(localPathPermissions.getCodeBase(), (Certificate[])null), localPathPermissions);
AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] { localProtectionDomain});
return localAccessControlContext;
}
static
{
ClassLoader.registerAsParallelCapable();
}
}
[3]. Launcher$AppClassLoader類的特點
{1}, 直接父類是URLClassLoader,而不是ExtClassLoader類
{2}. 重寫了父類的loadClass方法
public Class loadClass(String paramString, boolean paramBoolean) throws ClassNotFoundException{
int i = paramString.lastIndexOf('.');
if (i != -1) {
SecurityManager localSecurityManager = System.getSecurityManager();
if (localSecurityManager != null) {
localSecurityManager.checkPackageAccess(paramString.substring(0, i));
}
}
return super.loadClass(paramString, paramBoolean);
}
【注意】儘管是重寫父類的public的方法,但是僅僅做了一些類名上的檢查就再一次調用了父類的loadClass方法,沒有實質性的改變。調用了父類的loadClass就代表着在查找類的時候採用的是雙親委託機制進行查找。
(4). Launcher$ExtClassLoader類
[1]. 位置:sun.misc.Launcher的內部
[2]. ExtClassLoader源碼 (從Launcher內部摘取)
static class ExtClassLoader extendsURLClassLoader
{
public static ExtClassLoader getExtClassLoader() throws IOException{
File[] arrayOfFile = getExtDirs();
try{
return(ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction(arrayOfFile)
{
public Launcher.ExtClassLoader run() throws IOException {
int i = this.val$dirs.length;
for (int j = 0; j < i; j++) {
MetaIndex.registerDirectory(this.val$dirs[j]);
}
return new Launcher.ExtClassLoader(this.val$dirs);
} } );
}catch (PrivilegedActionExceptionlocalPrivilegedActionException) {
}
throw((IOException)localPrivilegedActionException.getException());
}
void addExtURL(URL paramURL)
{
super.addURL(paramURL);
}
public ExtClassLoader(File[] paramArrayOfFile)
throws IOException
{
super(null, Launcher.factory);
}
private static File[] getExtDirs() {
String str = System.getProperty("java.ext.dirs");
File[] arrayOfFile;
if (str != null) {
StringTokenizer localStringTokenizer = new StringTokenizer(str, File.pathSeparator);
int i =localStringTokenizer.countTokens();
arrayOfFile = new File[i];
for (int j = 0; j < i; j++)
arrayOfFile[j] = newFile(localStringTokenizer.nextToken());
}
else {
arrayOfFile = new File[0];
}
return arrayOfFile;
}
private static URL[] getExtURLs(File[] paramArrayOfFile) throws IOException {
Vector localVector = new Vector();
for (int i = 0; i < paramArrayOfFile.length; i++){
String[] arrayOfString = paramArrayOfFile[i].list();
if (arrayOfString != null) {
for (int j = 0; j < arrayOfString.length; j++) {
if (!arrayOfString[j].equals("meta-index")) {
File localFile = newFile(paramArrayOfFile[i], arrayOfString[j]);
localVector.add(Launcher.getFileURL(localFile));
}
}
}
}
URL[] arrayOfURL = newURL[localVector.size()];
localVector.copyInto(arrayOfURL);
return arrayOfURL;
}
public String findLibrary(String paramString)
{
paramString = System.mapLibraryName(paramString);
URL[] arrayOfURL = super.getURLs();
Object localObject = null;
for (int i = 0; i < arrayOfURL.length; i++)
{
File localFile1 = newFile(arrayOfURL[i].getPath()).getParentFile();
if ((localFile1 != null) &&(!localFile1.equals(localObject)))
{
String str = VM.getSavedProperty("os.arch");
if (str != null) {
localFile2 = new File(new File(localFile1, str), paramString);
if (localFile2.exists()) {
return localFile2.getAbsolutePath();
}
}
File localFile2 = newFile(localFile1, paramString);
if (localFile2.exists()) {
return localFile2.getAbsolutePath();
}
}
localObject = localFile1;
}
return null;
}
private static AccessControlContext getContext(File[]paramArrayOfFile)
throws IOException
{
PathPermissions localPathPermissions = new PathPermissions(paramArrayOfFile);
ProtectionDomain localProtectionDomain = new ProtectionDomain(new CodeSource(localPathPermissions.getCodeBase(),(Certificate[])null), localPathPermissions);
AccessControlContext localAccessControlContext = new AccessControlContext(new ProtectionDomain[] { localProtectionDomain});
return localAccessControlContext;
}
static
{
ClassLoader.registerAsParallelCapable();
}
}
[3].ExtClassLoader的特點
{1}. ExtClassLoader和AppClassLoader一樣,都是靜態內部類
{2}. ExtClassLoader和AppClassLoader一樣,直接父類都是URLClassLoader類。
由此我們可以得到如下的Java類加載器的真實繼承體系結構圖
3). Java中ClassLoader繼承體系
(1). ClassLoader及其子類的真實繼承體系
【注意】java.lang.ClassLoader是一個抽象類
(2). ExtClassLoader和AppClassLoader之間的關係
ExtClassLoader和AppClassLoader之間的所謂的“父子關係”
AppClassLoader是通過繼承ClassLoader基類內部聚合字段privatefinal ClassLoader parent; 來體現的ExtClassLoader是其“父類”。
【注意】由於Java僅僅支持單繼承,同時AppClassLoader已經繼承了URLClassLoader這個類,所以AppClassLoader就不能再使用extends繼承ExtClassLoader這個類。
但是爲了表達AppClassLoader和ExtClassLoader的關係,採用AppClassLoader的內部字段parent指向ExtClassLoader的實例。
(3). 雙親委託機制的細節
[1]. 當AppClassLoader被實例化時候,JVM調用AppClassLoader的loadClass方法對相應的類進行加載。
[2]. AppClassLoader重寫了父類的loadClass方法,但是在源代碼最後一行還是調用了super.loadClass( )。這證明了AppClassLoader使用的類加載的方式仍然是雙親委託模型。
[3]. 當調用父類的loadClass方法的時候,如果父類一直找不到要求加載的類,就會返回給子類進行加載。子類這個時候採用的是findClass方法進行類加載。
【注意】ClassLoader的默認findClass方法僅僅拋出異常,沒有去查找這個類!!
【注意】由於AppClassLoader和ExtClassLoader繼承的是URLClassLoader類,這個類卻重寫了ClassLoader這個類的findClass方法,所以調用AppClassLoader或者ExtClassLoader類的loadClass方法的時候,執行到findClass的時候,真正被JVM調用findClass是ClassLoader的子類URLClassLoader重寫的findClass方法。
----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------