Java原始碼解析(7) —— ClassLoader(2)
阿新 • • 發佈:2019-01-24
ClassLoader原始碼解析續
這一部分是ClassLoader核心部分,載入給定的資料成對應的類物件。
/**
* 由虛擬機器呼叫,這是一個private方法,但我在ClassLoader原始碼中並未看到有地方呼叫
* 看名字及原始碼說明,是由虛擬機器載入類的時候內部呼叫,百度查詢估計是載入jdk類時
* 系統類載入器通過反射呼叫該方法(jdk類中有許多類似用法)
*/
private Class loadClassInternal(String name)
throws ClassNotFoundException
{
if (parallelLockMap == null ) {
synchronized (this) {
return loadClass(name);
}
} else {
return loadClass(name);
}
}
// 根據安全域策略,檢查包許可權,同樣,由虛擬機器在載入一個類後呼叫
private void checkPackageAccess(Class cls, ProtectionDomain pd) {
final SecurityManager sm = System.getSecurityManager();
//如果存在安全管理器
if (sm != null) {
if (ReflectUtil.isNonPublicProxyClass(cls)) {
//代理類的走這邊,迴圈該類所實現的介面,對其進行相應的包許可權校驗
for (Class intf: cls.getInterfaces()) {
checkPackageAccess(intf, pd);
}
return;
}
final String name = cls.getName();
final int i = name.lastIndexOf('.');
if (i != -1) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
//具體的實現,由SecurityManager的native方法實現
sm.checkPackageAccess(name.substring(0, i));
return null;
}
}, new AccessControlContext(new ProtectionDomain[] {pd}));
}
}
domains.add(pd);
}
/**
* 類載入真正實現方法,由子類自己實現
* 一般真正實現會呼叫下方defineClass()相關方法
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
/**
* 將位元組陣列轉化為Class物件
* 已不推薦使用,其實現中的name=null,會直接丟擲異常
* 由下方defineClass(String, byte[], int, int)代替
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
*/
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}
/**
* 位元組流轉化為Java Class物件,也可以理解為將位元組流轉換成JVM位元組碼
* name是表示將對應的位元組流轉化為對應路徑類名的類的JVM位元組碼
*
* 這其中涉及到了安全策略、保護域、許可權等知識。
*
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
* @see java.security.CodeSource
* @see java.security.SecureClassLoader
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
//底層實現,檢視下方對應方法
return defineClass(name, b, off, len, null);
}
/**
* 決定類對應的保護域
* name是類的全路徑名
*/
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
//類名格式是否合法
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
//java開頭的包為系統類,無權更改
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
//檢查簽名
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}
private String defineClassSourceLocation(ProtectionDomain pd)
{
CodeSource cs = pd.getCodeSource();
String source = null;
if (cs != null && cs.getLocation() != null) {
source = cs.getLocation().toString();
}
return source;
}
//這個方法作用:當類載入錯誤(位元組資料格式錯誤等),嘗試重新定義類(重新生成Class物件)
private Class defineTransformedClass(String name, byte[] b, int off, int len,
ProtectionDomain pd,
ClassFormatError cfe, String source)
throws ClassFormatError
{
ClassFileTransformer[] transformers =
ClassFileTransformer.getTransformers();
Class c = null;
if (transformers != null) {
for (ClassFileTransformer transformer : transformers) {
try {
//使用對應的轉換器將對應位元組流轉換成相應(編碼?)的位元組流
//可能是對特定格式的位元組流的處理?
byte[] tb = transformer.transform(b, off, len);
c = defineClass1(name, tb, 0, tb.length,
pd, source);
break;
} catch (ClassFormatError cfe2) {
// 該轉換器無效,嘗試下一個
}
}
}
// 最終失敗,丟擲異常
if (c == null)
throw cfe;
return c;
}
//給類設定簽名,呼叫本地方法
private void postDefineClass(Class c, ProtectionDomain pd)
{
if (pd.getCodeSource() != null) {
Certificate certs[] = pd.getCodeSource().getCertificates();
if (certs != null)
setSigners(c, certs);
}
}
/**
* 載入對應類名Class
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
//檢查ClassLoader是否被初始化,未初始化丟擲未初始化異常
//另外,設定相應保護域
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
//呼叫native方法生成class檔案,native方法的具體實現與JVM相關
c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) {
//出錯,嘗試使用特定轉換器,重新載入
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source);
}
//設定簽名
postDefineClass(c, protectionDomain);
return c;
}
/**
* 載入緩衝區型別資料的類
* 類似上方方法,只是Class資料來源不同
*/
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// 虛擬機器記憶體,使用byte[]方式載入,即呼叫上方方法
if (!b.isDirect()) {
if (b.hasArray()) {
return defineClass(name, b.array(),
b.position() + b.arrayOffset(), len,
protectionDomain);
} else {
// 無資料格式,或只讀,需要將資料複製到另一塊區域內操作
byte[] tb = new byte[len];
b.get(tb);
return defineClass(name, tb, 0, len, protectionDomain);
}
}
//系統記憶體,載入方法
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
c = defineClass2(name, b, b.position(), len, protectionDomain,
source);
} catch (ClassFormatError cfe) {
byte[] tb = new byte[len];
b.get(tb);
c = defineTransformedClass(name, tb, 0, len, protectionDomain, cfe,
source);
}
postDefineClass(c, protectionDomain);
return c;
}
//本地方法,載入類
private native Class defineClass0(String name, byte[] b, int off, int len,
ProtectionDomain pd);
private native Class defineClass1(String name, byte[] b, int off, int len,
ProtectionDomain pd, String source);
private native Class defineClass2(String name, java.nio.ByteBuffer b,
int off, int len, ProtectionDomain pd,
String source);
// 校驗類名
private boolean checkName(String name) {
if ((name == null) || (name.length() == 0))
return true;
if ((name.indexOf('/') != -1)
|| (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
return false;
return true;
}