/**
* 类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。
* 如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。
* 一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。
* 每个 Class 对象都包含一个对定义它的 ClassLoader 的引用。
* 数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。
* 数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;
* 如果该元素类型是基本类型,则该数组类没有类加载器。
* 应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。
* 类加载器通常由安全管理器使用,用于指示安全域。
* ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。
* 需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。
* 虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,
* 但是可以将它用作 ClassLoader 实例的父类加载器。
* 通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。
* 例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。
* 然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。
* defineClass 方法将一个 byte 数组转换为 Class 类的实例。
* 这种新定义的类的实例可以使用 Class.newInstance 来创建。
* 类加载器所创建对象的方法和构造方法可以引用其他类。
* 为了确定引用的类,Java 虚拟机将调用最初创建该类的类加载器的 loadClass 方法。
*/
public abstract class ClassLoader {
// 要为类注册本地函数,RegisterNatives从本地的库中的本机代码调用JNI函数。
private static native void registerNatives();
static {
registerNatives();
}
/*
* 委托的父类装入器
* 注意:虚拟机硬编码这个字段的偏移量,
* 因此,所有新字段必须在*after*添加。
*/
private final ClassLoader parent;
/**
* 封装可并行加载器类型集
*/
private static class ParallelLoaders {
private ParallelLoaders() {}
// 一组具有并行能力的加载器类型
private static final Set> loaderTypes =
Collections.newSetFromMap(
new WeakHashMap, Boolean>());
static {
synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }
}
/**
* 将给定的类加载器类型注册为并行capabale。
* 成功注册返回true;
* 如果加载器的超类未注册,则为false。
*/
static boolean register(Class extends ClassLoader> c) {
synchronized (loaderTypes) {
if (loaderTypes.contains(c.getSuperclass())) {
/*
* 注册类加载器为并行的,当且仅当它的所有超类都是并行的。
* 注:给定当前类加载顺序,如果直接超类是并行的,
* 上面的所有超类也必须是。
*/
loaderTypes.add(c);
return true;
} else {
return false;
}
}
}
/**
* 如果指定的类加载器类型注册为并行的,则返回{true}。
*/
static boolean isRegistered(Class extends ClassLoader> c) {
synchronized (loaderTypes) {
return loaderTypes.contains(c);
}
}
}
/*
* 当当前类加载器具有并行能力时,将类名映射到相应的锁对象。
* 注意:VM还使用这个字段来决定当前类加载器是否具有并行能力,以及加载类的适当锁定对象。
*/
private final ConcurrentHashMap parallelLockMap;
// 将包映射到certs的哈希表
private final Map package2certs;
// 在所有无签名类的包之间共享
private static final Certificate[] nocerts = new Certificate[0];
/*
* 由这类加载器加载的类。
* 此表的唯一目的是在加载器GC之前防止类被GC。
*/
private final Vector> classes = new Vector<>();
/*
* “默认”domain。
* 设置为新创建的类的默认ProtectionDomain。
*/
private final ProtectionDomain defaultDomain =
new ProtectionDomain(new CodeSource(null, (Certificate[]) null),
null, this, null);
// 此加载程序加载的所有类的初始化保护domain
private final Set domains;
/**
* VM调用它来记录使用此加载器加载的每个类。
* @param c
*/
void addClass(Class> c) {
classes.addElement(c);
}
/*
* 类加载器中定义的包。
* 每个包名映射到对应的包对象。
*/
private final HashMap packages = new HashMap<>();
/**
* 检查创建新类加载器的权限
* @return
*/
private static Void checkCreateClassLoader() {
// 获取系统安全接口
SecurityManager security = System.getSecurityManager();
if (security != null) {
// 判断是否有调用线程创建新类加载器的权限,不允许,则抛SecurityException
security.checkCreateClassLoader();
}
return null;
}
/**
* 私有构造器
* @param unused
* @param parent
*/
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
// 如果指定的类加载器类型注册为并行的,则返回true。
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet());
assertionLock = new Object();
} else {
// 没有更细粒度的锁;锁定类加载器实例
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
/**
* 使用指定的、用于委托操作的父类加载器创建新的类加载器。
* 如果存在安全管理器,则调用其 checkCreateClassLoader 方法。这可能导致安全性异常。
* @param parent 父类加载器
*/
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
/**
* 使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器。
* 如果存在安全管理器,则调用其 checkCreateClassLoader 方法。这可能导致安全性异常。
*/
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
// -- Class --
/**
* 使用指定的二进制名称来加载类。此方法使用与 loadClass(String, boolean) 方法相同的方式搜索类。
* Java 虚拟机调用它来分析类引用。调用此方法等效于调用 loadClass(name, false)。
* @param name 类的二进制名称
* @return
* @throws ClassNotFoundException
*/
public Class> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* 使用指定的二进制名称来加载类。此方法的默认实现将按以下顺序搜索类:
* 调用 findLoadedClass(String) 来检查是否已经加载类。
* 在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
* 调用 findClass(String) 方法查找类。
* 如果使用上述步骤找到类,并且 resolve 标志为真,则此方法将在得到的 Class 对象上调用 resolveClass(Class) 方法。
* 鼓励用 ClassLoader 的子类重写 findClass(String),而不是使用此方法。
* @param name 类的二进制名称
* @param resolve 如果该参数为 true,则分析这个类
* @return
* @throws ClassNotFoundException
*/
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 {
// 如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法findBootstrapClass
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能
}
if (c == null) {
// 如果仍然没有找到,然后调用findClass来查找该类。
long t1 = System.nanoTime();
c = findClass(name);
// 这是定义类加载器;记录数据
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) { // 链接指定的类。
resolveClass(c);
}
return c;
}
}
/**
* 返回类加载操作的锁对象。
* 为了向后兼容,此方法的默认实现的行为如下。
* 如果这个类加载器对象被注册为并行的,该方法返回与指定类名关联的专用对象。否则,该方法返回这个类加载器对象。
* @param className 要加载的类的名称
* @return 类加载操作的锁
*/
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
Object newLock = new Object();
// 如果指定键已经不再与某个值相关联,则将它与给定值关联。
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
/**
* 虚拟机调用此方法来加载类。
* @param name 类的二进制名称
* @return
* @throws ClassNotFoundException
*/
private Class> loadClassInternal(String name)
throws ClassNotFoundException
{
// 为了向后兼容,当当前类加载器不支持并行时,显式锁定“this”。
if (parallelLockMap == null) {
synchronized (this) {
return loadClass(name);
}
} else {
return loadClass(name);
}
}
/**
* 在使用此加载器加载类之后由VM调用。
* @param cls
* @param pd
*/
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) {
/*
* 通过指定的 AccessControlContext 启用和限制特权,执行指定的 PrivilegedAction。
* 该操作在调用方保护域所拥有权限与指定的 AccessControlContext 表示的域所拥有权限的交集下执行。
*/
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
// 如果不允许调用线程访问由参数指定的包,则抛出 SecurityException。
sm.checkPackageAccess(name.substring(0, i));
return null;
}
// 创建带有给定 ProtectionDomain 集合的 AccessControlContext。上下文不能为 null。重复的域将从上下文中被移除。
}, new AccessControlContext(new ProtectionDomain[] {pd}));
}
}
domains.add(pd);
}
/**
* 使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。
* 在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。
* @param name 类的二进制名称
* @return
* @throws ClassNotFoundException
*/
protected Class> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
/**
* 将一个 byte 数组转换为 Class 类的实例。必须分析 Class,然后才能使用它。
* 此方法将默认的 ProtectionDomain 分配给新定义的类。
* 调用 Policy.getPolicy().getPermissions(new CodeSource(null, null)) 时,
* ProtectionDomain 被有效授予所返回的相同权限集。
* 默认域在第一次调用 defineClass 时创建,并在后续调用时被重用。
* 要将特定的 ProtectionDomain 分配给类,需要使用 defineClass 方法,
* 该方法将 ProtectionDomain 用作其参数之一。
* @param name 所需要的类的二进制名称,如果不知道此名称,则该参数为 null
* @param b 组成类数据的字节。
* @param off 类数据的 b 中的起始偏移量
* @param len 类数据的长度
* @return
* @throws ClassFormatError
*/
protected final Class> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
/**
* 确定ProtectionDomain,并检查。
* @param name
* @param pd
* @return
*/
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
//如果名称为null或可能为有效二进制名称,则为true
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// 指定的 name 不能以 "java." 开头,因为 "java.*" 包中的所有类都只能由引导类加载器定义。
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
}
// 如果 name 不是 null,则它必定等于由 byte 数组 "b" 指定的类的二进制名称
if (name != null) checkCerts(name, pd.getCodeSource());
return pd;
}
/**
* 获取与此 域的CodeSource 关联的位置
* @param pd
* @return
*/
private String defineClassSourceLocation(ProtectionDomain pd)
{
// 获取此域的 CodeSource。
CodeSource cs = pd.getCodeSource();
String source = null;
if (cs != null && cs.getLocation() != null) {
// 获取与此 CodeSource 关联的位置
source = cs.getLocation().toString();
}
return source;
}
/**
* 从该类的 ProtectionDomain 中的 CodeSource 可以获得类的证书集合。
* 添加到该包中的任何类都必须包含相同的证书集合
* @param c
* @param pd
*/
private void postDefineClass(Class> c, ProtectionDomain pd)
{
if (pd.getCodeSource() != null) {
// 获取与此 CodeSource 关联的证书。
Certificate certs[] = pd.getCodeSource().getCertificates();
if (certs != null)
// 设置类的签署者
setSigners(c, certs);
}
}
/**
* 使用可选的 ProtectionDomain 将一个 byte 数组转换为 Class 类的实例。
* 如果该域为 null,则将默认域分配给 defineClass(String, byte[], int, int) 的文档中指定的类。
* 这个类必须分析后才能使用。
* 包中定义的第一个类确定在该包中定义的所有后续类必须包含的证书的确切集合。
* 从该类的 ProtectionDomain 中的 CodeSource 可以获得类的证书集合。
* 添加到该包中的任何类都必须包含相同的证书集合,否则抛出 SecurityException 异常。
* 注意,如果 name 为 null,则不执行该检查。应该始终传入要定义的类的二进制名称以及字节。
* 这可确保定义该类的正确性。
* 指定的 name 不能以 "java." 开头,因为 "java.*" 包中的所有类都只能由引导类加载器定义。
* 如果 name 不是 null,则它必定等于由 byte 数组 "b" 指定的类的二进制名称,
* 否则将抛出 NoClassDefFoundError。
* @param name 所需类的二进制名称,如果不知道此名称,则该参数为 null
* @param b 组成类数据的字节。
* @param off 类数据的 b 中的起始偏移量
* @param len 类数据的长度
* @param protectionDomain 类的 ProtectionDomain
* @return
* @throws ClassFormatError
*/
protected final Class> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
// 确定ProtectionDomain,分析
protectionDomain = preDefineClass(name, protectionDomain);
// 获取与此 域的CodeSource 关联的位置
String source = defineClassSourceLocation(protectionDomain);
// 获取class对象,通过b为byte[]
Class> c = defineClass1(name, b, off, len, protectionDomain, source);
// 从该类的 ProtectionDomain 中的 CodeSource 可以获得类的证书集合。
// 添加到该包中的任何类都必须包含相同的证书集合
postDefineClass(c, protectionDomain);
return c;
}
/**
* 使用可选的 ProtectionDomain 将 ByteBuffer 转换为 Class 类的实例。
* 如果该域为 null,则将默认域分配给 defineClass(String, byte[], int, int) 的文档中指定的类。
* 这个类必须分析后才能使用。
* 有关包中定义的第一个类(它确定了包的证书集合)的规则,以及对类名称的限制,
* 都与 defineClass(String, byte[], int, int, ProtectionDomain) 的文档中指定的相同。
* @param name 所需要的类的二进制名称,如果不知道此名称,则该参数为 null
* @param b 组成类数据的字节。
* @param protectionDomain 类的 ProtectionDomain,或为 null。
* @return
* @throws ClassFormatError
*/
protected final Class> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
// 获取此缓冲区中的剩余元素数
int len = b.remaining();
// 如果不是直接ByteBufer,请使用byte[]:
// 判断此缓冲区是否为直接缓冲区。
if (!b.isDirect()) {
// 判断当且仅当此缓冲区有非只读的底层实现数组时返回 true。
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,并检查。
protectionDomain = preDefineClass(name, protectionDomain);
// 获取与此 域的CodeSource 关联的位置
String source = defineClassSourceLocation(protectionDomain);
// 获取class对象,通过b为nio.ByteBuffer字节缓冲
Class> c = defineClass2(name, b, b.position(), len, protectionDomain, source);
// 从该类的 ProtectionDomain 中的 CodeSource 可以获得类的证书集合。
// 添加到该包中的任何类都必须包含相同的证书集合
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);
/**
* 如果名称为null或可能为有效二进制名称,则为true
* @param name
* @return
*/
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;
}
/**
* 检查name是否等于由 byte 数组 "b" 指定的类的二进制名称
* @param name
* @param cs
*/
private void checkCerts(String name, CodeSource cs) {
int i = name.lastIndexOf('.');
String pname = (i == -1) ? "" : name.substring(0, i);
Certificate[] certs = null;
if (cs != null) {
// // 获取与此 CodeSource 关联的证书。
certs = cs.getCertificates();
}
Certificate[] pcerts = null;
if (parallelLockMap == null) {
// 放入与当前类加载器关联的package2certs中
synchronized (this) {
pcerts = package2certs.get(pname);
if (pcerts == null) {
package2certs.put(pname, (certs == null? nocerts:certs));
}
}
} else {
// 如果指定键已经不再与某个值相关联,则将它与给定值关联。
pcerts = ((ConcurrentHashMap)package2certs).
putIfAbsent(pname, (certs == null? nocerts:certs));
}
// 检查以确保新类(certs)的证书与包中插入的第一个类(pcerts)的证书相同
if (pcerts != null && !compareCerts(pcerts, certs)) {
throw new SecurityException("class \""+ name +
"\"'s signer information does not match signer information of other classes in the same package");
}
}
/**
* 检查以确保新类(certs)的证书与包中插入的第一个类(pcerts)的证书相同
* @param pcerts
* @param certs
* @return
*/
private boolean compareCerts(Certificate[] pcerts,
Certificate[] certs)
{
// certs 可以为null, 表示没有certs
if ((certs == null) || (certs.length == 0)) {
return pcerts.length == 0;
}
// 这一点的长度必须相同
if (certs.length != pcerts.length)
return false;
// 检查并确保一个数组中的所有certs都在另一个数组中,反之亦然。
boolean match;
for (int i = 0; i < certs.length; i++) {
match = false;
for (int j = 0; j < pcerts.length; j++) {
if (certs[i].equals(pcerts[j])) {
match = true;
break;
}
}
if (!match) return false;
}
// 现在对pcert做同样的事情
for (int i = 0; i < pcerts.length; i++) {
match = false;
for (int j = 0; j < certs.length; j++) {
if (pcerts[i].equals(certs[j])) {
match = true;
break;
}
}
if (!match) return false;
}
return true;
}
/**
* 链接指定的类。类加载器可以使用此方法(其名称容易使人误解)来链接类。
* 如果已经链接类 c,则仅返回此方法。
* @param c 要链接的类
*/
protected final void resolveClass(Class> c) {
resolveClass0(c);
}
private native void resolveClass0(Class> c);
/**
* 查找具有指定的二进制名称的类,必要时加载它。
* 此方法通过系统类加载器(参见 getSystemClassLoader())来加载该类。
* 返回的 Class 对象具有多个与之相关联的 ClassLoader。ClassLoader 的子类通常不必调用此方法,
* 因为大多数类加载器只需重写 findClass(String) 即可。
* @param name 类的二进制名称
* @return 指定 name 的 Class 对象
* @throws ClassNotFoundException
*/
protected final Class> findSystemClass(String name)
throws ClassNotFoundException
{
// 获取委托的系统类加载器。
ClassLoader system = getSystemClassLoader();
if (system == null) {
// 如果名称为null或可能为有效二进制名称,则为true
if (!checkName(name))
throw new ClassNotFoundException(name);
Class> cls = findBootstrapClass(name);
if (cls == null) {
throw new ClassNotFoundException(name);
}
return cls;
}
// 使用指定的二进制名称来加载类。
return system.loadClass(name);
}
/**
* 使用虚拟机的内置类加载器加载类
* @param name
* @return
*/
private Class> findBootstrapClassOrNull(String name)
{
// 如果名称为null或可能为有效二进制名称,则为true
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
// 如果没有找到,返回null
private native Class> findBootstrapClass(String name);
/**
* 如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,
* 则返回该二进制名称的类。否则,返回 null。
* @param name 类的二进制名称
* @return Class 对象,如果类没有被加载,则返回 null
*/
protected final Class> findLoadedClass(String name) {
// 如果名称为null或可能为有效二进制名称,则为true
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
private native final Class> findLoadedClass0(String name);
/**
* 设置类的签署者。应该在定义类后调用此方法。
* @param c Class 对象
* @param signers 类的签署者
*/
protected final void setSigners(Class> c, Object[] signers) {
c.setSigners(signers);
}
// -- Resource --
/**
* 查找具有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的一些数据(图像、声音、文本等)。
* 资源名称是以 '/' 分隔的标识资源的路径名称。 此方法首先搜索资源的父类加载器;
* 如果父类加载器为 null,则搜索的路径就是虚拟机的内置类加载器的路径。
* 如果搜索失败,则此方法将调用 findResource(String) 来查找资源。
* @param name 资源名称
* @return 读取资源的 URL 对象;如果找不到该资源,或者调用者没有足够的权限获取该资源,则返回 null。
*/
public URL getResource(String name) {
URL url;
// 搜索资源的父类加载器;
if (parent != null) {
url = parent.getResource(name);
} else {
// 如果父类加载器为 null,则搜索的路径就是虚拟机的内置类加载器的路径。
url = getBootstrapResource(name);
}
if (url == null) {
// 查找资源
url = findResource(name);
}
return url;
}
/**
* 查找所有给定名称的资源。资源是可以通过类代码以与代码基无关的方式访问的某些数据(图像、声音、文本等)。
* 资源名称是以 '/' 分隔的标识资源的路径名称。
* @param name 资源名称
* @return 资源的 URL 对象的枚举。如果找不到资源,则该枚举将为空。类加载器无权访问的资源不在此枚举中。
* @throws IOException
*/
public Enumeration getResources(String name) throws IOException {
@SuppressWarnings("unchecked")
Enumeration[] tmp = (Enumeration[]) new Enumeration>[2];
// 搜索资源的父类加载器;
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
// 如果父类加载器为 null,则搜索的路径就是虚拟机的内置类加载器的路径。
tmp[0] = getBootstrapResources(name);
}
// 查找资源
tmp[1] = findResources(name);
return new CompoundEnumeration<>(tmp);
}
/**
* 查找具有给定名称的资源。类加载器实现应该重写此方法,以指定从何处查找资源。
* @param name 资源名称
* @return 用于读取资源的 URL 对象;如果无法找到资源,则返回 null
*/
protected URL findResource(String name) {
return null;
}
/**
* 返回表示所有具有给定名称的资源的 URL 对象的枚举。类加载器实现应该重写此方法,以指定从何处加载资源。
* @param name 资源名称
* @return 资源的 URL 对象的枚举
* @throws IOException
*/
protected Enumeration findResources(String name) throws IOException {
return java.util.Collections.emptyEnumeration();
}
/**
* 将调用者注册为并行的。
* 注册成功当且仅当满足以下所有条件:
* 1. 没有创建调用者的实例
* 2. 调用者的所有超类(类对象除外)都被注册为并行的
* 请注意,一旦类装入器被注册为具有并行能力,就无法将其更改回来。
* @return 如果调用方成功注册为具有并行能力,则为true;如果调用方注册为具有并行能力,则为false。
*/
@CallerSensitive
protected static boolean registerAsParallelCapable() {
Class extends ClassLoader> callerClass =
Reflection.getCallerClass().asSubclass(ClassLoader.class);
// 将给定的类装入器类型注册为并行capabale。
return ParallelLoaders.register(callerClass);
}
/**
* 从用来加载类的搜索路径中查找具有指定名称的资源。
* 此方法通过系统类加载器(参见 getSystemClassLoader())来查找资源。
* @param name 资源名称
* @return 用于读取资源的 URL 对象,如果找不到资源,则返回 null
*/
public static URL getSystemResource(String name) {
// 获取委托的系统类加载器。
ClassLoader system = getSystemClassLoader();
if (system == null) {
// 获取虚拟机的内置类加载器。
return getBootstrapResource(name);
}
// 查找具有给定名称的资源。
return system.getResource(name);
}
/**
* 从用来加载类的搜索路径中查找所有具有指定名称的资源。
* 找到的资源作为 URL 对象的 Enumeration 返回。
* @param name 资源名称
* @return 资源 URL 对象的枚举
* @throws IOException
*/
public static Enumeration getSystemResources(String name)
throws IOException
{
// 获取委托的系统类加载器。
ClassLoader system = getSystemClassLoader();
if (system == null) {
// 获取虚拟机的内置类加载器。
return getBootstrapResources(name);
}
// 查找具有给定名称的资源。
return system.getResources(name);
}
/**
* 从VM的内置类加载器中查找资源。
* @param name 资源名称
* @return 用于读取资源的 URL 对象,如果找不到资源,则返回 null
*/
private static URL getBootstrapResource(String name) {
// 返回用于查找系统资源的URLClassPath。
URLClassPath ucp = getBootstrapClassPath();
Resource res = ucp.getResource(name);
return res != null ? res.getURL() : null;
}
/**
* 从VM的内置类加载器中查找资源。
* @param name 资源名称
* @return 资源 URL 对象的枚举
* @throws IOException
*/
private static Enumeration getBootstrapResources(String name)
throws IOException
{
final Enumeration e =
getBootstrapClassPath().getResources(name);
return new Enumeration () {
public URL nextElement() {
return e.nextElement().getURL();
}
public boolean hasMoreElements() {
return e.hasMoreElements();
}
};
}
/**
* 返回用于查找系统资源的URLClassPath。
* @return
*/
static URLClassPath getBootstrapClassPath() {
return sun.misc.Launcher.getBootstrapClassPath();
}
/**
* 返回读取指定资源的输入流。
* @param name 资源名称
* @return 用于读取资源的输入流,如果无法找到资源,则返回 null
*/
public InputStream getResourceAsStream(String name) {
// 查找具有给定名称的资源。
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
/**
* 从用来加载类的搜索路径打开具有指定名称的资源,以读取该资源。
* @param name 资源名称
* @return 用于读取资源的输入流,如果无法找到资源,则返回 null
*/
public static InputStream getSystemResourceAsStream(String name) {
// 从用来加载类的搜索路径中查找具有指定名称的资源。
URL url = getSystemResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
// -- Hierarchy --
/**
* 返回委托的父类加载器。一些实现可能使用 null 来表示引导类加载器。
* 如果类加载器的父类加载器就是引导类加载器,则此方法将在这样的实现中返回 null。
* 如果存在安全管理器,且调用者的类加载器既不是 null,也不是此类加载器的祖先,
* 那么此方法将使用 RuntimePermission("getClassLoader") 权限调用安全管理器的 checkPermission 方法,
* 以检验是否允许访问该类的父类加载器。如果无此权限,则抛出 SecurityException 异常。
* @return
*/
@CallerSensitive
public final ClassLoader getParent() {
if (parent == null)
return null;
// 获取系统安全接口
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
/*
* 检查对父类加载器的访问,
* 如果调用方的类加载器与此类加载器相同,执行权限检查。
*/
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}
/**
* 返回委托的系统类加载器。该加载器是新的 ClassLoader 实例的默认委托父类加载器,
* 通常是用来启动应用程序的类加载器。
* 在运行时启动序列的早期首先调用此方法,这时会创建系统类加载器并将其设置为调用 Thread 的上下文类加载器。
* 默认的系统类加载器是此类的一个与实现有关的实例。
* 如果在第一次调用此方法时定义系统属性 "java.system.class.loader",
* 那么该属性的值就是将作为系统类加载器返回的那个类的名称。
* 该类是使用默认系统类加载器进行加载的,它必须定义一个公共的构造方法,
* 此构造方法带有用作委托父类加载器的 ClassLoader 类型的单个参数。
* 然后可以使用将默认系统类加载器用作参数的此构造方法创建一个实例。得到的类加载器被定义为系统类加载器。
* 如果存在安全管理器,且调用者的类加载器既不是 null,也不同于或不是系统类加载器的祖先,
* 那么该方法将使用 RuntimePermission("getClassLoader") 权限调用安全管理器的 checkPermission 方法,
* 以检验系统类加载器的访问权。如果无此权限,则抛出 SecurityException 异常。
* @return 委托的系统 ClassLoader,如果没有这样的类加载器,则返回 null
*/
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
// 创建系统类加载器
initSystemClassLoader();
if (scl == null) {
return null;
}
// 获取系统安全接口
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
/*
* 检查对父类加载器的访问,
* 如果调用方的类加载器与此类加载器相同,执行权限检查。
*/
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
/**
* 创建系统类加载器
*/
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
// 返回类加载器
scl = l.getClassLoader();
try {
// 启用特权,执行指定的 PrivilegedAction。
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
// 返回此异常的原因
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// 包装异常
throw new Error(oops);
}
}
}
sclSet = true;
}
}
/**
* 如果指定的类加载器可以在该类加载器的委托链中找到,则返回true。
* @param cl
* @return
*/
boolean isAncestor(ClassLoader cl) {
ClassLoader acl = this;
do {
acl = acl.parent;
if (cl == acl) {
return true;
}
} while (acl != null);
return false;
}
/**
* 测试类加载器访问是否需要“getClassLoader”权限检查。
* 类加载器'from'可以访问类加载器'to',如果类加载器'from'与类加载器'to'或'to'的祖先相同的话。
* system domain中的类加载器可以访问任何类加载器。
* @param from
* @param to
* @return
*/
private static boolean needsClassLoaderPermissionCheck(ClassLoader from,
ClassLoader to)
{
if (from == to)
return false;
if (from == null)
return false;
// 如果指定的类加载器可以在该类加载器的委托链中找到,则返回true。
return !to.isAncestor(from);
}
/**
* 返回类加载器
* @param caller
* @return
*/
static ClassLoader getClassLoader(Class> caller) {
// 如果VM请求它,它可以是null
if (caller == null) {
return null;
}
// 绕过安全检查,因为这是包私有的
return caller.getClassLoader0();
}
/**
* 如果调用者的类加载器不为空,且调用者的类加载器与给定cl参数不相同或不是其祖先,
* 则检查运行时权限(“getClassLoader”)权限。
* @param cl
* @param caller
*/
static void checkClassLoaderPermission(ClassLoader cl, Class> caller) {
// 获取系统安全接口
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
//如果VM正在请求调用者,则调用者可以为空
ClassLoader ccl = getClassLoader(caller);
if (needsClassLoaderPermissionCheck(ccl, cl)) {
sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
}
}
}
/*
* 系统的类加载器
* @GuardedBy (“ClassLoader.class”)
*/
private static ClassLoader scl;
/*
* 一旦系统类加载器被设置为true
* @GuardedBy (“ClassLoader.class”)
*/
private static boolean sclSet;
// -- Package --
/**
* 根据 name 在此 ClassLoader 中定义包。这允许类加载器定义用于它们的类的包。
* 包必须在定义类之前创建,包名在类加载器中必须惟一,并且一旦创建就不能重新定义或更改。
* @param name 包名
* @param specTitle 规范标题
* @param specVersion 规范版本
* @param specVendor 规范供应商
* @param implTitle 实现标题
* @param implVersion 实现版本
* @param implVendor 实现供应商
* @param sealBase 如果不为 null,那么将此包对于给定代码源 URL 对象是密封的。否则,不密封此包。
* @return 新定义的 Package 对象
* @throws IllegalArgumentException
*/
protected Package definePackage(String name, String specTitle,
String specVersion, String specVendor,
String implTitle, String implVersion,
String implVendor, URL sealBase)
throws IllegalArgumentException
{
synchronized (packages) {
// 获取由此类加载器或其任何祖先所定义的 Package
Package pkg = getPackage(name);
if (pkg != null) {
throw new IllegalArgumentException(name);
}
pkg = new Package(name, specTitle, specVersion, specVendor,
implTitle, implVersion, implVendor,
sealBase, this);
packages.put(name, pkg);
return pkg;
}
}
/**
* 返回由此类加载器或其任何祖先所定义的 Package。
* @param name 包名
* @return 与给定名称相对应的 Package;如果没有这样的包,则返回 null
*/
protected Package getPackage(String name) {
Package pkg;
synchronized (packages) {
pkg = packages.get(name);
}
if (pkg == null) {
if (parent != null) {
// 获取祖先所定义的 Package
pkg = parent.getPackage(name);
} else {
// 返回指定名称的已加载系统包。
pkg = Package.getSystemPackage(name);
}
if (pkg != null) {
synchronized (packages) {
Package pkg2 = packages.get(name);
if (pkg2 == null) {
packages.put(name, pkg);
} else {
pkg = pkg2;
}
}
}
}
return pkg;
}
/**
* 返回此类加载器及其祖先所定义的所有 Package
* @return
*/
protected Package[] getPackages() {
Map map;
synchronized (packages) {
map = new HashMap<>(packages);
}
Package[] pkgs;
if (parent != null) {
// 返回祖先所定义的所有 Package
pkgs = parent.getPackages();
} else {
// 返回已加载系统包的数组。
pkgs = Package.getSystemPackages();
}
if (pkgs != null) {
for (int i = 0; i < pkgs.length; i++) {
String pkgName = pkgs[i].getName();
if (map.get(pkgName) == null) {
map.put(pkgName, pkgs[i]);
}
}
}
return map.values().toArray(new Package[map.size()]);
}
// -- Native library access --
/**
* 返回本机库的绝对路径名。虚拟机 (VM) 调用此方法来查找那些属于利用此类加载器加载的类的本机库。
* 如果此方法返回 null,则虚拟机沿指定为 "java.library.path" 属性的路径搜索该库。
* @param libname 库名称
* @return 本机库的绝对路径
*/
protected String findLibrary(String libname) {
return null;
}
/**
* 内部类NativeLibrary表示加载的本机库实例。
* 每个类加载器在私有字段nativeLibraries中包含一个已加载本机库的向量。
* 加载到系统中的本机库被输入到systemNativeLibraries vector。
*
* 每个本机库都需要特定版本的JNI。
* 这由私有的JNI Version字段表示。
* 这个字段是VM在加载库时设置的,
* 并由VM用于将正确版本的JNI传递给本机方法。
*/
static class NativeLibrary {
// 本机库的不透明句柄,在本机代码中使用。
long handle;
// 本机库需要的JNI环境版本。
private int jniVersion;
// 加载库的类还指示此本机库所属的加载器。
private final Class> fromClass;
//本机库的规范化名称。或静态库名
String name;
// 指示本机库是否链接到VM
boolean isBuiltin;
// 指示是否加载本机库
boolean loaded;
native void load(String name, boolean isBuiltin);
native long find(String name);
native void unload(String name, boolean isBuiltin);
public NativeLibrary(Class> fromClass, String name, boolean isBuiltin) {
this.name = name;
this.fromClass = fromClass;
this.isBuiltin = isBuiltin;
}
protected void finalize() {
synchronized (loadedLibraryNames) {
if (fromClass.getClassLoader() != null && loaded) {
/* 删除本机库名称 */
int size = loadedLibraryNames.size();
for (int i = 0; i < size; i++) {
if (name.equals(loadedLibraryNames.elementAt(i))) {
loadedLibraryNames.removeElementAt(i);
break;
}
}
/* 卸载库。 */
ClassLoader.nativeLibraryContext.push(this);
try {
unload(name, isBuiltin);
} finally {
ClassLoader.nativeLibraryContext.pop();
}
}
}
}
// 在VM中调用,以确定JNI_Load/JNI_Unload中的上下文类
static Class> getFromClass() {
return ClassLoader.nativeLibraryContext.peek().fromClass;
}
}
// 我们加载的所有本机库名称。
private static Vector loadedLibraryNames = new Vector<>();
// 属于系统类的本机库。
private static Vector systemNativeLibraries
= new Vector<>();
// 与类装载器关联的本机库。
private Vector nativeLibraries = new Vector<>();
// 正在加载/卸载本机库。
private static Stack nativeLibraryContext = new Stack<>();
// 路径搜索库
private static String usr_paths[];
private static String sys_paths[];
/**
* 初始化库路径
* @param propname
* @return
*/
private static String[] initializePath(String propname) {
// 获取指定键指示的系统属性
String ldpath = System.getProperty(propname, "");
String ps = File.pathSeparator;
int ldlen = ldpath.length();
int i, j, n;
// 计算路径中的分隔符
i = ldpath.indexOf(ps);
n = 0;
while (i >= 0) {
n++;
i = ldpath.indexOf(ps, i + 1);
}
// 分配路径数组- n:'s = n + 1路径元素
String[] paths = new String[n + 1];
// 用ldpath中的路径填充数组
n = i = 0;
j = ldpath.indexOf(ps);
while (j >= 0) {
if (j - i > 0) {
paths[n++] = ldpath.substring(i, j);
} else if (j - i == 0) {
paths[n++] = ".";
}
i = j + 1;
j = ldpath.indexOf(ps, i);
}
paths[n] = ldpath.substring(i, ldlen);
return paths;
}
/**
* 在java.lang中调用。运行时类来实现load和loadLibrary。
* @param fromClass
* @param name
* @param isAbsolute
*/
static void loadLibrary(Class> fromClass, String name,
boolean isAbsolute) {
ClassLoader loader =
(fromClass == null) ? null : fromClass.getClassLoader();
if (sys_paths == null) {
usr_paths = initializePath("java.library.path");
sys_paths = initializePath("sun.boot.library.path");
}
if (isAbsolute) {
if (loadLibrary0(fromClass, new File(name))) {
return;
}
throw new UnsatisfiedLinkError("Can't load library: " + name);
}
if (loader != null) {
// 返回本机库的绝对路径名。
String libfilename = loader.findLibrary(name);
if (libfilename != null) {
File libfile = new File(libfilename);
// 测试此抽象路径名是否为绝对路径名。
if (!libfile.isAbsolute()) {
throw new UnsatisfiedLinkError(
"ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
}
if (loadLibrary0(fromClass, libfile)) {
return;
}
throw new UnsatisfiedLinkError("Can't load " + libfilename);
}
}
for (int i = 0 ; i < sys_paths.length ; i++) {
// 加载由 libname 参数指定的系统库。
File libfile = new File(sys_paths[i], System.mapLibraryName(name));
if (loadLibrary0(fromClass, libfile)) {
return;
}
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
if (libfile != null && loadLibrary0(fromClass, libfile)) {
return;
}
}
if (loader != null) {
for (int i = 0 ; i < usr_paths.length ; i++) {
// 加载由 libname 参数指定的用户库。
File libfile = new File(usr_paths[i],
System.mapLibraryName(name));
if (loadLibrary0(fromClass, libfile)) {
return;
}
// 返回给定文件的备用路径名
libfile = ClassLoaderHelper.mapAlternativeName(libfile);
if (libfile != null && loadLibrary0(fromClass, libfile)) {
return;
}
}
}
// 失败
throw new UnsatisfiedLinkError("no " + name + " in java.library.path");
}
private static native String findBuiltinLib(String name);
private static boolean loadLibrary0(Class> fromClass, final File file) {
// 检查是否试图访问静态库
String name = findBuiltinLib(file.getName());
boolean isBuiltin = (name != null);
if (!isBuiltin) {
boolean exists = AccessController.doPrivileged(
new PrivilegedAction
参考文档:JDK 1.6