类加载器的核心源码
2024年11月29日大约 3 分钟
类加载器的加载类的方法
loadClass()
方法是类加载的入口,提供了双亲委派机制,它会检查类是否被加载,以及交给父类加载或者交给启动类加载此类,如果都失败,则执行自己的加载方法,主要是调用findClass方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//首先,检查类是否被加载了
Class<?> c = findLoadedClass(name);
//如果类没有被加载
if (c == null) {
long t0 = System.nanoTime();
try {
//有父类,让委派给父类进行加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//没有父类,交给启动类加载器加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
//如果仍然没有被加载,则调用findClass方法找到该类
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//如果传递的resolve是true,则会执行生命周期中的连接阶段
resolveClass(c);
}
return c;
}
}
findClass()
方法的主要目的是查找指定路径的资源:资源找到之后执行defineClass()
方法
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
//AccessController.doPrivileged:以受信任的权限上下文执行代码,避免安全管理器阻止某些操作
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<>() {
public Class<?> run() throws ClassNotFoundException {
//将类名转换为路径格式:
String path = name.replace('.', '/').concat(".class");
//从类路径中查找指定路径的资源
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
//资源查找成功则执行defineClass()方法;
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
} catch (ClassFormatError e2) {
if (res.getDataError() != null) {
e2.addSuppressed(res.getDataError());
}
throw e2;
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
defineClass()
方法的具体实现如下,负责将字节码定义为 Java 的 Class 对象
private Class<?> defineClass(String name, Resource res) throws IOException {
long t0 = System.nanoTime();
//找到类名中最后一个.的位置,用于获取类的包名
int i = name.lastIndexOf('.');
URL url = res.getCodeSourceURL();
if (i != -1) {
String pkgname = name.substring(0, i);
// Check if package 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 and define the class
//从资源中获取类的字节码
java.nio.ByteBuffer bb = res.getByteBuffer();
if (bb != null) {
// 获取代码签名者信息
CodeSigner[] signers = res.getCodeSigners();
//获取类的来源信息
CodeSource cs = new CodeSource(url, signers);
PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
//调用重载方法,使用ByteBuffer定义类
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// 必须在读取字节后读取证书。
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
//调用重载方法:使用字节数组定义类
return defineClass(name, b, 0, b.length, cs);
}
}
defineClass()
重载的方法中的操作:使用字节码将类加载到 JVM 的内存中,并返回 Class 对象。它通过调用底层的本地方法实现具体的类定义,同时提供了一些钩子方法(如 preDefineClass 和 postDefineClass)以便在定义类之前和之后执行额外逻辑。
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// Use byte[] if not a direct ByteBuffer:
if (!b.isDirect()) {
if (b.hasArray()) {
return defineClass(name, b.array(),
b.position() + b.arrayOffset(), len,
protectionDomain);
} else {
// no array, or read-only array
byte[] tb = new byte[len];
b.get(tb); // get bytes out of byte buffer.
return defineClass(name, tb, 0, len, protectionDomain);
}
}
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass2(this, name, b, b.position(), len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}