【Java原理系列】Java中ClassLoader原理用法示例中文源码分析
**类加载器是负责加载类的对象**。ClassLoader类是一个**抽象类**。给定类的二进制名称,类加载器应该尝试定位或生成构成该类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
Java中ClassLoader原理用法示例中文源码分析
必看
类加载器是负责加载类的对象。ClassLoader类是一个抽象类。 给定类的二进制名称,类加载器应该尝试定位或生成构成该类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
每个Class对象都包含指向定义它的ClassLoader的引用。
数组类的Class对象不是由类加载器创建的,而是在Java运行时根据需要自动创建的。通过getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同; 如果元素类型是原始类型,则数组类没有类加载器。
应用程序可以实现ClassLoader的子类以扩展Java虚拟机动态加载类的方式。
类加载器通常由安全管理器用于指示安全域。
ClassLoader类使用委托模型来搜索类和资源。每个ClassLoader实例都有一个关联的父类加载器。在请求查找类或资源时,ClassLoader实例将在尝试自己查找类或资源之前将类或资源的查找委托给其父类加载器。虚拟机内置的类加载器称为“引导类加载器”,它本身没有父类加载器,但可以作为ClassLoader实例的父类。
支持并发加载类的类加载器称为并行可加载类加载器, 它们需要在其类初始化时通过调用ClassLoader.registerAsParallelCapable()方法进行自我注册。 请注意,默认情况下,ClassLoader类已经注册为并行可加载。 然而,如果子类是并行可加载的,则仍然需要注册自己。在非严格层次结构的环境中,类加载器需要具备并行可加载的能力,否则类加载可能会导致死锁,
因为在整个类加载过程中保持了加载器锁(参见loadClass方法)。
通常,Java虚拟机以平台相关的方式从本地文件系统加载类。 例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录加载类。但是,有些类可能不来自文件;它们可能来自其他来源,如网络,或者可以由应用程序构造。 defineClass方法将字节数组转换为Class实例。 可以使用这个新定义的类的实例来创建对象。
类加载器创建的对象的方法和构造函数可能引用其他类。 为了确定所引用的类,Java虚拟机会调用最初创建该类的类加载器的loadClass方法。
例如,一个应用程序可以创建一个网络类加载器来从服务器下载类文件。 示例代码如下:
ClassLoader loader = new NetworkClassLoader(host, port);
Object main = loader.loadClass("Main", true).newInstance();
网络类加载器子类必须定义findClass方法和loadClassData方法来从网络中加载类。 下载组成该类的字节后,应使用defineClass方法创建一个类实例。示例实现如下:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// 从连接中加载类数据
...
}
}
在ClassLoader的方法中,作为String参数提供的任何类名都必须是由Java语言规范定义的二进制名称。
二进制名称有效类名的示例包括:
“java.lang.String”, “javax.swing.JSpinner
D
e
f
a
u
l
t
E
d
i
t
o
r
"
,
"
j
a
v
a
.
s
e
c
u
r
i
t
y
.
K
e
y
S
t
o
r
e
DefaultEditor", "java.security.KeyStore
DefaultEditor","java.security.KeyStoreBuilder$FileBuilder$1”
“java.net.URLClassLoader$3$1”
原理
Java中的ClassLoader类是用于加载Java类的核心类之一。它负责在运行时查找和加载Java类的字节码,并将其转换为可以在Java虚拟机中执行的可执行代码。
ClassLoader的工作原理如下:
-
类加载委派模型:ClassLoader使用一种称为类加载委派模型的机制来加载类。当应用程序请求加载一个类时,ClassLoader首先检查它是否已经被加载过,如果是,则直接返回该类的定义。如果没有加载过,则将请求传递给父级ClassLoader。这个过程会一直向上层ClassLoader进行传递,直到达到顶级的引导类加载器(Bootstrap ClassLoader)。如果顶级引导类加载器无法找到类的定义,那么会回到初始的ClassLoader,尝试自己加载类。
-
双亲委派模型:ClassLoader通过双亲委派模型来实现类加载的安全性。根据这个模型,ClassLoader在尝试加载类之前,会先委托给其父级ClassLoader去加载。这样可以确保类的加载是从上到下、从外到内的有序加载。这种机制可以防止恶意代码通过替换系统类库中的类来破坏Java运行环境的安全性。
-
查找类文件:当一个ClassLoader需要加载一个类时,它首先会根据类的全限定名转换成类文件的路径。ClassLoader会根据指定的类加载路径(Classpath)来查找类文件。一般情况下,ClassLoader会从文件系统或者JAR文件中查找类文件。
-
定义类:当ClassLoader找到类文件后,会读取类文件的字节码,并将其转换为Java虚拟机可执行的格式。然后使用定义类的方法(defineClass())将字节码转换为Java类的定义,并返回一个Class对象。
总之,ClassLoader负责在Java运行时动态地查找、加载和定义类。它通过委派模型和双亲委派机制保证了类加载的顺序和安全性。ClassLoader的工作原理是Java语言实现动态加载和扩展性的重要基础。
用法
以下是对ClassLoader类全部用法的详细描述:
方法 | 描述 |
---|---|
loadClass(String name) | 加载指定名称的类,并返回对应的Class对象。这个方法使用双亲委派模型,从上至下依次尝试加载类。如果找不到类,则会抛出ClassNotFoundException异常。 |
findClass(String name) | 查找并加载指定名称的类,并返回对应的Class对象。这个方法一般在自定义ClassLoader中重写,以实现自定义的类查找逻辑。如果找不到类,则需要抛出ClassNotFoundException异常。 |
defineClass(String name, byte[] b, int off, int len) | 将字节数组转换为Java类的定义,并返回对应的Class对象。这个方法通常在自定义ClassLoader中被调用,用于加载已经获得的类字节码。 |
getParent() | 获取当前ClassLoader的父级ClassLoader。ClassLoader在加载类时会首先委托给父级ClassLoader去加载。如果没有父级ClassLoader,则返回null。 |
getSystemClassLoader() | 返回系统默认的ClassLoader。这是应用程序的默认ClassLoader,用于加载类路径上的类。 |
getClassLoader() | 获取给定类的ClassLoader。这个方法可以用来获取任意类的ClassLoader,例如通过Class对象的getClassLoader()方法来获取该类的ClassLoader。 |
setDefaultAssertionStatus(boolean enabled) | 设置类加载器的默认断言状态。断言状态决定由ClassLoader加载的类是否默认启用或禁用断言。 |
setPackageAssertionStatus(String packageName, boolean enabled) | 设置指定包的断言状态。可以通过这个方法来控制指定包及其子包下的类是否启用或禁用断言。 |
setClassAssertionStatus(String className, boolean enabled) | 设置指定类的断言状态。可以通过这个方法来控制指定类是否启用或禁用断言。 |
clearAssertionStatus() | 清除类加载器的断言状态,将其重置为默认值。这会清除所有已设置的包和类的断言状态设置。 |
ClassLoader类提供了许多用于加载、查找和定义类的方法,以及管理断言状态的功能。这些方法允许开发人员根据自己的需求进行类加载和管理操作,并实现自定义的类加载逻辑。
示例
下面是一些ClassLoader类的代码示例,展示了其主要用法:
- 加载类:
ClassLoader classLoader = MyClass.class.getClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.MyClass");
- 查找类:
ClassLoader classLoader = MyClass.class.getClassLoader();
Class<?> clazz = classLoader.findClass("com.example.MyClass");
- 自定义ClassLoader并重写findClass()方法:
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 自定义类查找逻辑,根据名称加载类字节码
byte[] classBytes = loadClassBytes(name);
return defineClass(name, classBytes, 0, classBytes.length);
}
}
// 使用自定义ClassLoader加载类
MyClassLoader classLoader = new MyClassLoader();
Class<?> clazz = classLoader.loadClass("com.example.MyClass");
- 获取父级ClassLoader:
ClassLoader parentClassLoader = MyClass.class.getClassLoader().getParent();
- 获取系统ClassLoader:
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
- 获取类加载器链:
Class<?> clazz = MyClass.class;
ClassLoader classLoader = clazz.getClassLoader();
while (classLoader != null) {
System.out.println(classLoader);
classLoader = classLoader.getParent();
}
- 设置默认断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.setDefaultAssertionStatus(true);
- 设置包和类的断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.setPackageAssertionStatus("com.example", true);
classLoader.setClassAssertionStatus("com.example.MyClass", false);
- 清除断言状态:
ClassLoader classLoader = MyClass.class.getClassLoader();
classLoader.clearAssertionStatus();
这些示例展示了ClassLoader类的一些常见用法,涵盖了加载、查找、定义类以及断言状态管理等功能。根据具体需求,可以选择适合的方法来实现所需的类加载和管理操作。
中文源码
/**
* 类加载器是负责加载类的对象。ClassLoader类是一个抽象类。
* 给定类的二进制名称,类加载器应该尝试定位或生成构成该类定义的数据。
* 典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
*
* 每个Class对象都包含指向定义它的ClassLoader的引用。
*
* 数组类的Class对象不是由类加载器创建的,而是在Java运行时根据需要自动创建的。
* 通过getClassLoader()返回的数组类的类加载器与其元素类型的类加载器相同;
* 如果元素类型是原始类型,则数组类没有类加载器。
*
* 应用程序可以实现ClassLoader的子类以扩展Java虚拟机动态加载类的方式。
*
* 类加载器通常由安全管理器用于指示安全域。
*
* ClassLoader类使用委托模型来搜索类和资源。
* 每个ClassLoader实例都有一个关联的父类加载器。
* 在请求查找类或资源时,ClassLoader实例将在尝试自己查找类或资源之前将类或资源的查找委托给其父类加载器。
* 虚拟机内置的类加载器称为“引导类加载器”,它本身没有父类加载器,但可以作为ClassLoader实例的父类。
*
* 支持并发加载类的类加载器称为并行可加载类加载器,
* 它们需要在其类初始化时通过调用ClassLoader.registerAsParallelCapable()方法进行自我注册。
* 请注意,默认情况下,ClassLoader类已经注册为并行可加载。
* 然而,如果子类是并行可加载的,则仍然需要注册自己。
* 在非严格层次结构的环境中,类加载器需要具备并行可加载的能力,否则类加载可能会导致死锁,
* 因为在整个类加载过程中保持了加载器锁(参见loadClass方法)。
*
* 通常,Java虚拟机以平台相关的方式从本地文件系统加载类。
* 例如,在UNIX系统上,虚拟机从CLASSPATH环境变量定义的目录加载类。
*
* 但是,有些类可能不来自文件;它们可能来自其他来源,如网络,或者可以由应用程序构造。
* defineClass方法将字节数组转换为Class实例。
* 可以使用这个新定义的类的实例来创建对象。
*
* 类加载器创建的对象的方法和构造函数可能引用其他类。
* 为了确定所引用的类,Java虚拟机会调用最初创建该类的类加载器的loadClass方法。
*
* 例如,一个应用程序可以创建一个网络类加载器来从服务器下载类文件。
* 示例代码如下:
*
* ClassLoader loader = new NetworkClassLoader(host, port);
* Object main = loader.loadClass("Main", true).newInstance();
* ...
*
* 网络类加载器子类必须定义findClass方法和loadClassData方法来从网络中加载类。
* 下载组成该类的字节后,应使用defineClass方法创建一个类实例。示例实现如下:
*
* class NetworkClassLoader extends ClassLoader {
* String host;
* int port;
*
* public Class findClass(String name) {
* byte[] b = loadClassData(name);
* return defineClass(name, b, 0, b.length);
* }
*
* private byte[] loadClassData(String name) {
* // 从连接中加载类数据
* ...
* }
* }
*
* 二进制名称
*
* 在ClassLoader的方法中,作为String参数提供的任何类名都必须是由Java语言规范定义的二进制名称。
*
* 有效类名的示例包括:
* "java.lang.String"
* "javax.swing.JSpinner$DefaultEditor"
* "java.security.KeyStore$Builder$FileBuilder$1"
* "java.net.URLClassLoader$3$1"
*
* @see #resolveClass(Class)
* @since 1.0
*/
public abstract class ClassLoader {
private static native void registerNatives();
static {
registerNatives();
}
// 用于委派的父类加载器
// 注意:虚拟机硬编码了这个字段的偏移量,因此所有新字段必须添加到它之后。
private final ClassLoader parent;
/**
* 封装并行可加载器类型集合。
*/
private static class ParallelLoaders {
private ParallelLoaders() {}
// 并行可加载器类型集合
private static final Set<Class<? extends ClassLoader>> loaderTypes =
Collections.newSetFromMap(
new WeakHashMap<Class<? extends ClassLoader>, Boolean>());
static {
synchronized (loaderTypes) { loaderTypes.add(ClassLoader.class); }
}
/**
* 注册给定的类加载器类型为并行可加载。
* 如果成功注册,则返回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);
}
}
}
// 当前类加载器支持并发加载类时,将类名映射到相应的锁对象。
// 注意:虚拟机还使用此字段来确定当前类加载器是否支持并发加载,并找到类加载的适当锁对象。
private final ConcurrentHashMap<String, Object> parallelLockMap;
// 将包名映射到相应证书的哈希表
private final Map <String, Certificate[]> package2certs;
// 所有无证书类共享的数组
private static final Certificate[] nocerts = new Certificate[0];
// 由该类加载器加载的类。此表的唯一目的是保持类不被GC,直到类加载器被GC。
private final Vector<Class<?>> classes = new Vector<>();
// 此类加载器定义的包。每个包名称都映射到其对应的Package对象。
private final HashMap<String, Package> packages = new HashMap<>();
private static Void checkCreateClassLoader() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkCreateClassLoader();
}
return null;
}
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
assertionLock = new Object();
} else {
// 没有更细粒度的锁;锁定类加载器实例本身
parallelLockMap = null;
package2certs = new Hashtable<>();
assertionLock = this;
}
}
/**
* 使用指定的父类加载器创建一个新的类加载器。
*
* 如果存在安全管理器,则调用其checkCreateClassLoader方法。
* 这可能会导致安全异常。
*
* @param parent
* 父类加载器
*
* @throws SecurityException
* 如果存在安全管理器并且其checkCreateClassLoader方法不允许创建新的类加载器。
*
* @since 1.2
*/
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
/**
* 使用由getSystemClassLoader()方法返回的ClassLoader作为父类加载器创建一个新的类加载器。
*
* 如果存在安全管理器,则调用其checkCreateClassLoader方法。
* 这可能会导致安全异常。
*
* @throws SecurityException
* 如果存在安全管理器并且其checkCreateClassLoader方法不允许创建新的类加载器。
*/
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
// -- Class --
/**
* 加载具有指定二进制名称的类。
* 此方法与loadClass(String, boolean)方法相同地搜索类。
* 它是Java虚拟机解析类引用时调用的。
* 调用此方法等效于调用loadClass(name, false)。
*
* @param name
* 类的二进制名称
*
* @return 结果的Class对象
*
* @throws ClassNotFoundException
* 如果找不到类
*/
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* 使用指定的二进制名称加载类。
* 此方法的默认实现按以下顺序搜索类:
*
* 1. 调用findLoadedClass(String)检查类是否已经被加载。
* 2. 在父类加载器上调用loadClass(String)方法。
* 如果父类加载器为null,则使用虚拟机内置的类加载器。
* 3. 调用findClass(String)方法查找类。
*
* 如果使用上述步骤找到了类,并且resolve标志为true,
* 则此方法将在生成的Class对象上调用resolveClass(Class)方法。
*
* 鼓励ClassLoader的子类重写findClass(String)方法,而不是这个方法。
*
* 除非重写,否则此方法在整个类加载过程中都会同步于getClassLoadingLock(String)方法的结果。
*
* @param name
* 类的二进制名称
*
* @param resolve
* 是否解析类
*
* @return 结果的Class对象
*
* @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 {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父类加载器不为空,从父类加载器中找不到类,则抛出ClassNotFoundException异常
}
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;
}
}
/**
* 返回类加载操作的锁对象。
* 为了向后兼容性,此方法的默认实现如下所示:
* 如果此ClassLoader对象已注册为并行可加载,则该方法返回与指定类名相关联的专用对象。
* 否则,该方法返回此ClassLoader对象。
*
* @param className
* 要加载的类的名称
*
* @return 类加载操作的锁
*
* @throws NullPointerException
* 如果已注册为并行可加载并且className为null
*
* @see #loadClass(String, boolean)
*
* @since 1.7
*/
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;
}
// JVM在使用此加载器加载类后调用此方法。
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() {
sm.checkPackageAccess(name.substring(0, i));
return null;
}
}, new AccessControlContext(new ProtectionDomain[] {pd}));
}
}
}
/**
* 查找具有指定二进制名称的类。
* 此方法应该由按照委派模型加载类的类加载器实现重写,
* 并且将在loadClass方法检查父类加载器是否找到所请求的类后调用。
* 默认实现会抛出ClassNotFoundException异常。
*
* @param name
* 类的二进制名称
*
* @return 结果的Class对象
*
* @throws ClassNotFoundException
* 如果找不到类
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
/**
* 将字节数组转换为Class实例。
* 在使用Class之前,必须解析Class。
*
* <p>此方法为新定义的类分配了默认的ProtectionDomain。
* 该ProtectionDomain实际上被授予与调用Policy.getPolicy().getPermissions(new CodeSource(null, null))时返回的权限集相同的权限。
* 默认域在第一次调用defineClass方法时创建,并在后续调用中重复使用。
*
* <p>要为类分配特定的ProtectionDomain,请使用接受ProtectionDomain作为其参数之一的defineClass方法。
*
* @param name
* 类的预期二进制名称,如果未知则为null
*
* @param b
* 组成类数据的字节。位于b[off]到b[off+len-1]的位置的字节应该具有由Java虚拟机规范定义的有效类文件格式。
*
* @param off
* 类数据在b中的起始偏移量
*
* @param len
* 类数据的长度
*
* @return 从指定的类数据创建的Class对象
*
* @throws ClassFormatError
* 如果数据不包含有效的类
*
* @throws IndexOutOfBoundsException
* 如果off或len为负数,或者off+len大于b.length
*
* @throws SecurityException
* 如果尝试将此类添加到包中,该包包含了与此类不同的一组证书签名的类,
* 或者如果尝试在完全限定名称以“java.”开头的包中定义类。
*
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
*
* @deprecated 使用defineClass(String, byte[], int, int)方法替代
*/
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}
/**
* 将字节数组转换为Class实例。
* 在使用Class之前,必须解析Class。
*
* <p>此方法为新定义的类分配了默认的ProtectionDomain。
* 该ProtectionDomain实际上被授予与调用Policy.getPolicy().getPermissions(new CodeSource(null, null))时返回的权限集相同的权限。
* 默认域在第一次调用defineClass方法时创建,并在后续调用中重复使用。
*
* @param name
* 类的预期二进制名称,如果未知则为null
*
* @param b
* 组成类数据的字节。位于b[off]到b[off+len-1]的位置的字节应该具有由Java虚拟机规范定义的有效类文件格式。
*
* @param off
* 类数据在b中的起始偏移量
*
* @param len
* 类数据的长度
*
* @return 从指定的类数据创建的Class对象
*
* @throws ClassFormatError
* 如果数据不包含有效的类
*
* @throws IndexOutOfBoundsException
* 如果off或len为负数,或者off+len大于b.length
*
* @throws SecurityException
* 如果尝试将此类添加到包中,该包包含了与此类不同的一组证书签名的类,
* 或者如果尝试在完全限定名称以“java.”开头的包中定义类。
*
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
* @see java.security.CodeSource
* @see java.security.SecureClassLoader
*
* @since 1.1
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(name, b, off, len, null);
}
// 确定保护域,并检查:
// - 不定义java.*类,
// - 此类的签名者与包中其他类的签名者匹配。
private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
// 注意:java.lang.invoke.MemberName.checkForTypeAlias中的检查逻辑依赖于以下事实:如果一个类具有"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;
}
private void postDefineClass(Class<?> c, ProtectionDomain pd)
{
if (pd.getCodeSource() != null) {
Certificate certs[] = pd.getCodeSource().getCertificates();
if (certs != null)
setSigners(c, certs);
}
}
/**
* 将字节数组转换为<tt>Class</tt>类的实例,可以附加<tt>ProtectionDomain</tt>。
* 如果域为<tt>null</tt>,则将为该类分配一个默认的域,如{@link #defineClass(String, byte[], int, int)}中所述。
* 在使用该类之前,必须对其进行解析。
*
* <p>在一个包中定义的第一个类决定了该包中所有后续定义的类必须包含的确切证书集合。
* 类的证书集合是从类的<tt>ProtectionDomain</tt>中的{@link java.security.CodeSource <tt>CodeSource</tt>}获得的。
* 添加到该包中的任何类都必须包含相同的证书集合,否则将抛出<tt>SecurityException</tt>异常。
* 注意,如果<tt>name</tt>为<tt>null</tt>,则不执行此检查。
* 您应始终传递要定义的类的<a href="#name">二进制名称</a>以及字节。
* 这样可以确保要定义的类确实是您认为的那个类。
*
* <p>指定的<tt>name</tt>不能以“<tt>java.</tt>”开头,因为“<tt>java.*</tt>”包中的所有类只能由引导类加载器定义。
* 如果<tt>name</tt>不是<tt>null</tt>,则它必须等于通过字节数组“<tt>b</tt>”指定的类的<a href="#name">二进制名称</a>,否则将抛出{@link NoClassDefFoundError <tt>NoClassDefFoundError</tt>}异常。
*
* @param name
* 类的期望<a href="#name">二进制名称</a>,如果未知,则为<tt>null</tt>
*
* @param b
* 组成类数据的字节数组。从位置<tt>off</tt>到<tt>off+len-1</tt>的字节应具有有效类文件的格式,如<cite>Java™虚拟机规范</cite>中所定义。
*
* @param off
* 类数据在<tt>b</tt>中的起始偏移量
*
* @param len
* 类数据的长度
*
* @param protectionDomain
* 类的ProtectionDomain
*
* @return 从数据创建的<tt>Class</tt>对象和可选的<tt>ProtectionDomain</tt>
*
* @throws ClassFormatError
* 如果数据不包含有效的类
*
* @throws NoClassDefFoundError
* 如果<tt>name</tt>不等于<tt>b</tt>指定的类的<a href="#name">二进制名称</a>
*
* @throws IndexOutOfBoundsException
* 如果<tt>off</tt>或<tt>len</tt>为负数,或者<tt>off+len</tt>大于<tt>b.length</tt>
*
* @throws SecurityException
* 如果尝试将此类添加到包中,该包包含由与此类不同的证书集签名的类,或者如果<tt>name</tt>以“<tt>java.</tt>”开头
*/
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
/**
* 将{@link java.nio.ByteBuffer <tt>ByteBuffer</tt>}转换为<tt>Class</tt>类的实例,可以附加<tt>ProtectionDomain</tt>。
* 如果域为<tt>null</tt>,则将为该类分配一个默认的域,如{@link #defineClass(String, byte[], int, int)}中所述。
* 在使用该类之前,必须对其进行解析。
*
* <p>关于在包中定义的第一个类确定包的证书集合的规则以及类名的限制与{@link #defineClass(String, byte[], int, int, ProtectionDomain)}中的文档中指定的相同。
*
* <p>以<i>cl</i><tt>.defineClass(</tt><i>name</i><tt>,</tt>
* <i>bBuffer</i><tt>,</tt> <i>pd</i><tt>)</tt>形式调用此方法将产生与以下语句完全相同的结果
*
*<p> <tt>
* ...<br>
* byte[] temp = new byte[bBuffer.{@link
* java.nio.ByteBuffer#remaining remaining}()];<br>
* bBuffer.{@link java.nio.ByteBuffer#get(byte[])
* get}(temp);<br>
* return {@link #defineClass(String, byte[], int, int, ProtectionDomain)
* cl.defineClass}(name, temp, 0,
* temp.length, pd);<br>
* </tt></p>
*
* @param name
* 类的期望<a href="#name">二进制名称</a>,如果未知,则为<tt>null</tt>
*
* @param b
* 组成类数据的字节数组。从位置<tt>b.position()</tt>到<tt>b.position() + b.limit() -1</tt>的字节应具有有效类文件的格式,如<cite>Java™虚拟机规范</cite>中所定义。
*
* @param protectionDomain
* 类的ProtectionDomain,或<tt>null</tt>
*
* @return 从数据创建的<tt>Class</tt>对象和可选的<tt>ProtectionDomain</tt>
*
* @throws ClassFormatError
* 如果数据不包含有效的类
*
* @throws NoClassDefFoundError
* 如果<tt>name</tt>不等于<tt>b</tt>指定的类的<a href="#name">二进制名称</a>
*
* @throws SecurityException
* 如果尝试将此类添加到包中,该包包含由与此类不同的证书集签名的类,或者如果<tt>name</tt>以“<tt>java.</tt>”开头
*
* @see #defineClass(String, byte[], int, int, ProtectionDomain)
*
* @since 1.5
*/
protected final Class<?> defineClass(String name, java.nio.ByteBuffer b,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
int len = b.remaining();
// 如果不是直接的ByteBufer,则使用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);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass2(name, b, b.position(), len, protectionDomain, 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);
// 如果名称为null或潜在是有效的二进制名称,则为true
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;
}
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) {
certs = cs.getCertificates();
}
Certificate[] pcerts = null;
if (parallelLockMap == null) {
synchronized (this) {
pcerts = package2certs.get(pname);
if (pcerts == null) {
package2certs.put(pname, (certs == null? nocerts:certs));
}
}
} else {
pcerts = ((ConcurrentHashMap<String, Certificate[]>)package2certs).
putIfAbsent(pname, (certs == null? nocerts:certs));
}
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)的证书相同。
*/
private boolean compareCerts(Certificate[] pcerts,
Certificate[] certs)
{
// 证书可以为null,表示没有证书。
if ((certs == null) || (certs.length == 0)) {
return pcerts.length == 0;
}
// 此时长度必须相同
if (certs.length != pcerts.length)
return false;
// 检查一个数组中的所有证书是否都在另一个数组中,并反之亦然。
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;
}
// 现在对于pcerts执行相同的操作
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>已经被链接,那么这个方法只是简单地返回。否则,该类将按照<c>中的“执行”章节中所述进行链接。
*
* @param c
* 要链接的类
*
* @throws NullPointerException
* 如果<c>是<tt>null</tt>
*
* @see #defineClass(String, byte[], int, int)
*/
protected final void resolveClass(Class<?> c) {
resolveClass0(c);
}
private native void resolveClass0(Class<?> c);
/**
* 查找具有指定<a href="#name">二进制名称</a>的类,如果需要加载它。
*
* <p>该方法通过系统类加载器(参见{@link #getSystemClassLoader()})加载类。返回的<tt>Class</tt>对象可能与一个以上的<tt>ClassLoader</tt>关联。
* <tt>ClassLoader</tt>的子类通常不需要调用此方法,因为大多数类加载器只需覆盖{@link #findClass(String)}。</p>
*
* @param name
* 类的<a href="#name">二进制名称</a>
*
* @return 指定<tt>name</tt>的<tt>Class</tt>对象
*
* @throws ClassNotFoundException
* 如果找不到类
*
* @see #ClassLoader(ClassLoader)
* @see #getParent()
*/
protected final Class<?> findSystemClass(String name)
throws ClassNotFoundException
{
ClassLoader system = getSystemClassLoader();
if (system == null) {
if (!checkName(name))
throw new ClassNotFoundException(name);
Class<?> cls = findBootstrapClass(name);
if (cls == null) {
throw new ClassNotFoundException(name);
}
return cls;
}
return system.loadClass(name);
}
/**
* 返回由引导类加载器加载的类;如果未找到,则返回null。
*/
private Class<?> findBootstrapClassOrNull(String name)
{
if (!checkName(name)) return null;
return findBootstrapClass(name);
}
// 如果未找到,则返回null
private native Class<?> findBootstrapClass(String name);
/**
* 如果Java虚拟机将其记录为具有该<a href="#name">二进制名称</a>的类的启动加载器的初始化加载器,则返回具有给定<a href="#name">二进制名称</a>的类。
* 否则,返回<tt>null</tt>。
*
* @param name
* 类的<a href="#name">二进制名称</a>
*
* @return <tt>Class</tt>对象,如果未加载类则返回<tt>null</tt>
*
* @since 1.1
*/
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
private native final Class<?> findLoadedClass0(String name);
/**
* 设置类的签名者。在定义类后应调用此方法。
*
* @param c
* <tt>Class</tt>对象
*
* @param signers
* 类的签名者
*
* @since 1.1
*/
protected final void setSigners(Class<?> c, Object[] signers) {
c.setSigners(signers);
}
// -- 资源 --
/**
* 查找具有给定名称的资源。资源是一些数据(图像、音频、文本等),可以由类代码以与代码位置无关的方式访问。
*
* <p>资源的名称是一个'/'分隔的路径名,用于标识资源。</p>
*
* <p>此方法首先搜索父类加载器中的资源;如果父级为<tt>null</tt>,则搜索内置于虚拟机中的类加载器的路径。如果找不到资源,则该方法将调用{@link #findResource(String)}来查找资源。</p>
*
* @apiNote 当重写此方法时,建议实现确保任何委托与{@link #getResources(java.lang.String) getResources(String)}方法一致。
*
* @param name
* 资源名称
*
* @return 用于读取资源的<tt>URL</tt>对象,如果找不到资源或调用者没有足够的权限获取资源,则返回<tt>null</tt>
*
* @since 1.1
*/
public URL getResource(String name) {
URL url;
if (parent != null) {
url = parent.getResource(name);
} else {
url = getBootstrapResource(name);
}
if (url == null) {
url = findResource(name);
}
return url;
}
/**
* 查找具有给定名称的所有资源。资源是一些数据(图像、音频、文本等),可以由类代码以与代码位置无关的方式访问。
*
* <p>资源的名称是一个'/'分隔的路径名,用于标识资源。</p>
*
* <p>搜索顺序在{@link #getResource(String)}的文档中描述。</p>
*
* @param name
* 资源名称
*
* @return 资源的{@link java.net.URL <tt>URL</tt>}对象的枚举。如果找不到资源,则枚举为空。类加载器无法访问的资源将不在枚举中。
*
* @throws IOException
* 如果发生I/O错误
* @since 1.2
*/
public Enumeration<URL> getResources(String name) throws IOException {
@SuppressWarnings("unchecked")
Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
if (parent != null) {
tmp[0] = parent.getResources(name);
} else {
tmp[0] = getBootstrapResources(name);
}
tmp[1] = findResources(name);
return new CompoundEnumeration<>(tmp);
}
/**
* 查找具有给定名称的资源。类加载器实现应覆盖此方法以指定资源的查找位置。
*
* @param name
* 资源名称
*
* @return 用于读取资源的<tt>URL</tt>对象,如果找不到资源则返回<tt>null</tt>
*
* @since 1.2
*/
protected URL findResource(String name) {
return null;
}
/**
* 返回表示具有给定名称的所有资源的{@link java.net.URL <tt>URL</tt>}对象的枚举。类加载器实现应覆盖此方法以指定从何处加载资源。
*
* @param name
* 资源名称
*
* @return 用于资源的{@link java.net.URL <tt>URL</tt>}对象的枚举
*
* @throws IOException
* 如果发生I/O错误
*
* @since 1.2
*/
protected Enumeration<URL> findResources(String name) throws IOException {
return java.util.Collections.emptyEnumeration();
}
/**
* 将调用者注册为并行可用。
* 当且仅当满足以下所有条件时,注册才成功:
* <ol>
* <li> 调用者的任何实例尚未创建</li>
* <li> 调用者的所有超类(除了Object类)都已注册为并行可用</li>
* </ol>
* <p>请注意,一旦将类加载器注册为并行可用,就无法更改它。</p>
*
* @return 如果成功将调用者注册为并行可用,则返回true;否则返回false。
*
* @since 1.7
*/
@CallerSensitive
protected static boolean registerAsParallelCapable() {
Class<? extends ClassLoader> callerClass =
Reflection.getCallerClass().asSubclass(ClassLoader.class);
return ParallelLoaders.register(callerClass);
}
/**
* 从用于加载类的搜索路径中查找具有指定名称的资源。此方法通过系统类加载器(参见{@link #getSystemClassLoader()})定位资源。
*
* @param name
* 资源名称
*
* @return 用于读取资源的{@link java.net.URL <tt>URL</tt>}对象,如果找不到资源则返回<tt>null</tt>
*
* @since 1.1
*/
public static URL getSystemResource(String name) {
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResource(name);
}
return system.getResource(name);
}
/**
* 从用于加载类的搜索路径中查找具有指定名称的所有资源。找到的资源将作为{@link java.util.Enumeration <tt>Enumeration</tt>}的{@link java.net.URL <tt>URL</tt>}对象返回。
*
* <p>搜索顺序在{@link #getSystemResource(String)}的文档中描述。</p>
*
* @param name
* 资源名称
*
* @return 资源{@link java.net.URL <tt>URL</tt>}对象的枚举
*
* @throws IOException
* 如果发生I/O错误
* @since 1.2
*/
public static Enumeration<URL> getSystemResources(String name)
throws IOException
{
ClassLoader system = getSystemClassLoader();
if (system == null) {
return getBootstrapResources(name);
}
return system.getResources(name);
}
/**
* 从VM的内置类加载器中查找资源。
*/
private static URL getBootstrapResource(String name) {
URLClassPath ucp = getBootstrapClassPath();
Resource res = ucp.getResource(name);
return res != null ? res.getURL() : null;
}
/**
* 从VM的内置类加载器中查找资源。
*/
private static Enumeration<URL> getBootstrapResources(String name)
throws IOException
{
final Enumeration<Resource> e =
getBootstrapClassPath().getResources(name);
return new Enumeration<URL> () {
public URL nextElement() {
return e.nextElement().getURL();
}
public boolean hasMoreElements() {
return e.hasMoreElements();
}
};
}
// 返回用于查找系统资源的URLClassPath。
static URLClassPath getBootstrapClassPath() {
return sun.misc.Launcher.getBootstrapClassPath();
}
/**
* 返回用于读取指定资源的输入流。
*
* <p>搜索顺序在{@link #getResource(String)}的文档中描述。</p>
*
* @param name
* 资源名称
*
* @return 用于读取资源的输入流,如果找不到资源则返回<tt>null</tt>
*
* @since 1.1
*/
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
/**
* 打开以读取类加载器用于加载类的搜索路径中具有指定名称的资源。此方法通过系统类加载器(参见{@link #getSystemClassLoader()})定位资源。
*
* @param name
* 资源名称
*
* @return 用于读取资源的输入流,如果找不到资源则返回<tt>null</tt>
*
* @since 1.1
*/
public static InputStream getSystemResourceAsStream(String name) {
URL url = getSystemResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
// -- 层次结构 --
/**
* 返回用于委托的父类加载器。某些实现可能使用<tt>null</tt>表示引导类加载器。如果此类加载器的父级是引导类加载器,则在此类加载器中,此方法将返回<tt>null</tt>。
*
* <p>如果存在安全管理器,并且调用者的类加载器不是<tt>null</tt>并且不是此类加载器的祖先,则此方法将使用{@link
* SecurityManager#checkPermission(java.security.Permission)
* <tt>checkPermission</tt>}方法调用安全管理器的以<tt>RuntimePermission("getClassLoader")</tt>权限验证对父类加载器的访问是否被允许。否则,将抛出<tt>SecurityException</tt>。</p>
*
* @return 父级<tt>ClassLoader</tt>
*
* @throws SecurityException
* 如果存在安全管理器并且其<tt>checkPermission</tt>方法不允许对此类加载器的父类加载器的访问。
*
* @since 1.2
*/
@CallerSensitive
public final ClassLoader getParent() {
if (parent == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 检查对父类加载器的访问权限
// 如果调用者的类加载器与此类加载器相同,则进行权限检查。
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}
/**
* 返回用于委派的系统类加载器。这是新的ClassLoader实例的默认委派父级,并且通常是启动应用程序时使用的类加载器。
*
* <p>此方法在运行时的早期启动序列中首先调用,此时它创建系统类加载器并将其设置为调用线程的上下文类加载器。
*
* <p>默认的系统类加载器是此类的特定实现。
*
* <p>如果在首次调用此方法时定义了系统属性“java.system.class.loader”,则该属性的值将作为系统类加载器返回。该类使用默认的系统类加载器进行加载,并且必须定义一个以类型为ClassLoader的单个参数的公共构造函数,该参数用作委派父级。然后使用默认的系统类加载器作为参数使用此构造函数创建一个实例。生成的类加载器被定义为系统类加载器。
*
* <p>如果存在安全管理器,并且调用者的类加载器不为null且调用者的类加载器与系统类加载器不同且不是系统类加载器的祖先,则此方法将使用SecurityManager的{@link SecurityManager#checkPermission(java.security.Permission)<tt> checkPermission </ tt>}方法以{@link RuntimePermission#RuntimePermission(String)<tt> RuntimePermission( "getClassLoader")</tt>}权限验证对系统类加载器的访问权限。否则,将抛出SecurityException。</p>
*
* @return 委派的系统<tt> ClassLoader </ tt>,如果没有则为<tt> null </ tt>
*
* @throws SecurityException 如果存在安全管理器,并且其<tt> checkPermission </ tt>方法不允许访问系统类加载器。
*
* @throws IllegalStateException 如果在由“java.system.class.loader”属性指定的类加载器的构造过程中递归调用
*
* @throws Error 如果定义了系统属性“java.system.class.loader”,但无法加载命名类,提供程序类未定义所需构造函数或在调用该构造函数时引发异常。可以通过{@link Throwable#getCause()}方法检索错误的根本原因。
*
* @revised 1.4
*/
@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 {
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 {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}
// 如果在此类加载器的委派链中找到指定的类加载器,则返回true。
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'的祖先。系统域中的类加载器可以访问任何类加载器。
private static boolean needsClassLoaderPermissionCheck(ClassLoader from,
ClassLoader to)
{
if (from == to)
return false;
if (from == null)
return false;
return !to.isAncestor(from);
}
// 返回类的类加载器,如果没有则返回null。
static ClassLoader getClassLoader(Class<?> caller) {
// 如果VM请求该项,则这个值可能为null
if (caller == null) {
return null;
}
// 由于这是包私有的,所以绕过安全检查
return caller.getClassLoader0();
}
/*
* 检查RuntimePermission(“getClassLoader”)权限
* 如果调用者的类加载器不为null且调用者的类加载器与给定的cl参数不同且不是相同的或给定的cl参数的祖先。
*/
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 --
/**
* 在此<tt> ClassLoader </ tt>中按名称定义包。这允许类加载器为其类定义包。必须在定义类之前创建包,并且包名在类加载器中必须是唯一的,一旦创建就无法重新定义或更改。
*
* @param name 包名
*
* @param specTitle 规范标题
*
* @param specVersion 规范版本
*
* @param specVendor 规范供应商
*
* @param implTitle 实现标题
*
* @param implVersion 实现版本
*
* @param implVendor 实现供应商
*
* @param sealBase 如果不是null,则此包使用与给定代码源{@link java.net.URL <tt> URL </ tt>}对象相关联的封闭基础库。否则,该包未封装。
*
* @return 新定义的<tt> Package </ tt>对象
*
* @throws IllegalArgumentException 如果包名与此类加载器或其祖先之一中的现有包重复
*
* @since 1.2
*/
protected Package definePackage(String name, String specTitle,
String specVersion, String specVendor,
String implTitle, String implVersion,
String implVendor, URL sealBase)
throws IllegalArgumentException
{
synchronized (packages) {
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;
}
}
/**
* 返回由此类加载器或任何其祖先定义的<tt> Package </ tt>。
*
* @param name 包名
*
* @return 对应于给定名称的<tt> Package </ tt>,如果未找到则为<tt> null </ tt>
*
* @since 1.2
*/
protected Package getPackage(String name) {
Package pkg;
synchronized (packages) {
pkg = packages.get(name);
}
if (pkg == null) {
if (parent != null) {
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;
}
/**
* 返回由此类加载器及其祖先定义的所有<tt> Package </ tt>。
*
* @return 由此<tt> ClassLoader </ tt>定义的<tt> Package </ tt>对象数组
*
* @since 1.2
*/
protected Package[] getPackages() {
Map<String, Package> map;
synchronized (packages) {
map = new HashMap<>(packages);
}
Package[] pkgs;
if (parent != null) {
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调用此方法来定位属于使用此类加载器加载的类的本地库。如果此方法返回<tt> null </ tt>,则VM将沿着指定为“<tt> java.library.path </ tt>”属性的路径搜索库。
*
* @param libname 库名称
*
* @return 本地库的绝对路径
*
* @see System#loadLibrary(String)
* @see System#mapLibraryName(String)
*
* @since 1.2
*/
protected String findLibrary(String libname) {
return null;
}
/**
* NativeLibrary内部类表示已加载的本地库实例。
* 每个类加载器都包含一个加载的本地库向量,存储在private字段<tt>nativeLibraries</tt>中。系统加载的本地库被输入到<tt>systemNativeLibraries</tt>向量中。
*
* <p>每个本地库都需要特定版本的JNI。这由私有字段<tt>jniVersion</tt>表示。VM在加载库时设置此字段,并且VM使用它将正确的JNI版本传递给本地方法。</p>
*
* @see ClassLoader
* @since 1.2
*/
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) {
/* remove the native library name */
int size = loadedLibraryNames.size();
for (int i = 0; i < size; i++) {
if (name.equals(loadedLibraryNames.elementAt(i))) {
loadedLibraryNames.removeElementAt(i);
break;
}
}
/* unload the library. */
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<String> loadedLibraryNames = new Vector<>();
// 属于系统类的本地库。
private static Vector<NativeLibrary> systemNativeLibraries = new Vector<>();
// 与类加载器关联的本地库。
private Vector<NativeLibrary> nativeLibraries = new Vector<>();
// 正在加载/卸载的本地库。
private static Stack<NativeLibrary> nativeLibraryContext = new Stack<>();
// 用于搜索库的路径
private static String usr_paths[];
private static String sys_paths[];
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.Runtime类中调用以实现load和loadLibrary。
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++) {
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++) {
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;
}
}
}
// Oops, it failed
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<Object>() {
public Object run() {
return file.exists() ? Boolean.TRUE : null;
}})
!= null;
if (!exists) {
return false;
}
try {
name = file.getCanonicalPath();
} catch (IOException e) {
return false;
}
}
ClassLoader loader =
(fromClass == null) ? null : fromClass.getClassLoader();
Vector<NativeLibrary> libs =
loader != null ? loader.nativeLibraries : systemNativeLibraries;
synchronized (libs) {
int size = libs.size();
for (int i = 0; i < size; i++) {
NativeLibrary lib = libs.elementAt(i);
if (name.equals(lib.name)) {
return true;
}
}
synchronized (loadedLibraryNames) {
if (loadedLibraryNames.contains(name)) {
throw new UnsatisfiedLinkError
("Native Library " +
name +
" already loaded in another classloader");
}
/* If the library is being loaded (must be by the same thread,
* because Runtime.load and Runtime.loadLibrary are
* synchronous). The reason is can occur is that the JNI_OnLoad
* function can cause another loadLibrary invocation.
*
* Thus we can use a static stack to hold the list of libraries
* we are loading.
*
* If there is a pending load operation for the library, we
* immediately return success; otherwise, we raise
* UnsatisfiedLinkError.
*/
int n = nativeLibraryContext.size();
for (int i = 0; i < n; i++) {
NativeLibrary lib = nativeLibraryContext.elementAt(i);
if (name.equals(lib.name)) {
if (loader == lib.fromClass.getClassLoader()) {
return true;
} else {
throw new UnsatisfiedLinkError
("Native Library " +
name +
" is being loaded in another classloader");
}
}
}
NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
nativeLibraryContext.push(lib);
try {
lib.load(name, isBuiltin);
} finally {
nativeLibraryContext.pop();
}
if (lib.loaded) {
loadedLibraryNames.addElement(name);
libs.addElement(lib);
return true;
}
return false;
}
}
}
// 在VM类链接代码中调用。
static long findNative(ClassLoader loader, String name) {
Vector<NativeLibrary> libs =
loader != null ? loader.nativeLibraries : systemNativeLibraries;
synchronized (libs) {
int size = libs.size();
for (int i = 0; i < size; i++) {
NativeLibrary lib = libs.elementAt(i);
long entry = lib.find(name);
if (entry != 0)
return entry;
}
}
return 0;
}
// -- 断言管理 --
final Object assertionLock;
// 断言检查的默认切换。
// @GuardedBy("assertionLock")
private boolean defaultAssertionStatus = false;
// 将String packageName映射到Boolean package默认断言状态。
// 注意,默认包被放置在null映射键下。如果此字段为null,则我们将断言状态查询委托给VM,即没有此ClassLoader的断言状态修改方法之一已被调用。
// @GuardedBy("assertionLock")
private Map<String, Boolean> packageAssertionStatus = null;
// 将String fullyQualifiedClassName映射到Boolean assertionStatus。如果此字段为null,则我们将断言状态查询委托给VM,即没有此ClassLoader的断言状态修改方法之一已被调用。
// @GuardedBy("assertionLock")
Map<String, Boolean> classAssertionStatus = null;
/**
* 设置此类加载器的默认断言状态。此设置确定由此类加载器加载并在将来初始化的类是否默认启用或禁用断言。
* 可以通过调用{@link #setPackageAssertionStatus(String, boolean)}或{@link #setClassAssertionStatus(String, boolean)}来覆盖此设置。
*
* @param enabled
* 如果由此类加载器加载的类将默认启用断言,则为<tt>true</tt>,如果它们将默认禁用断言,则为<tt>false</tt>。
*
* @since 1.4
*/
public void setDefaultAssertionStatus(boolean enabled) {
synchronized (assertionLock) {
if (classAssertionStatus == null)
initializeJavaAssertionMaps();
defaultAssertionStatus = enabled;
}
}
/**
* 设置指定包的包默认断言状态。包默认断言状态确定将来初始化的属于指定包或其“子包”中的类的断言状态。
*
* <p> 名为p的包的子包是任何名称以"<tt>p.</tt>"开头的包。例如,<tt>javax.swing.text</tt>是<tt>javax.swing</tt>的子包,<tt>java.util</tt>和<tt>java.lang.reflect</tt>都是<tt>java</tt>的子包。
*
* <p> 如果多个包默认适用于给定类,则与最特定包相关联的包默认优先于其他包。例如,如果<tt>javax.lang</tt>和<tt>javax.lang.reflect</tt>都有与之关联的包默认值,则后一个包默认值适用于<tt>javax.lang.reflect</tt>中的类。
*
* <p> 包默认优先于类加载器的默认断言状态,并且可以通过调用{@link #setClassAssertionStatus(String, boolean)}在每个类的基础上进行覆盖。</p>
*
* @param packageName
* 要设置其包默认断言状态的包的名称。null值表示未命名的包,即“当前”包(参见<cite>The Java™ Language Specification</cite>第7.4.2节)。
*
* @param enabled
* 如果由此类加载器加载并且属于指定包或其子包中的类将默认启用断言,则为<tt>true</tt>;如果它们将默认禁用断言,则为<tt>false</tt>。
*
* @since 1.4
*/
public void setPackageAssertionStatus(String packageName,
boolean enabled) {
synchronized (assertionLock) {
if (packageAssertionStatus == null)
initializeJavaAssertionMaps();
packageAssertionStatus.put(packageName, enabled);
}
}
/**
* 设置此类加载器中指定顶级类及其包含的任何嵌套类的所需断言状态。该设置优先于类加载器的默认断言状态和适用的每个包默认值。如果已经初始化了命名类,则此方法无效。(一旦类被初始化,其断言状态就不能更改。)
*
* <p> 如果命名类不是顶级类,则此调用对任何类的实际断言状态没有影响。</p>
*
* @param className
* 要设置其断言状态的顶级类的完全限定类名。
*
* @param enabled
* 如果在初始化时启用(如果有的话),则命名类将启用断言;如果禁用断言,则类将禁用断言。
*
* @since 1.4
*/
public void setClassAssertionStatus(String className, boolean enabled) {
synchronized (assertionLock) {
if (classAssertionStatus == null)
initializeJavaAssertionMaps();
classAssertionStatus.put(className, enabled);
}
}
/**
* 将此类加载器的默认断言状态设置为<tt>false</tt>,并丢弃与类加载器关联的任何包默认或类断言状态设置。
* 提供此方法是为了使类加载器可以忽略任何命令行或持久的断言状态设置,并“从一个干净的板开始。”
*
* @since 1.4
*/
public void clearAssertionStatus() {
/*
* 不管是否初始化了“Java断言映射”,都将其设置为空映射,从而有效地忽略任何现有设置。
*/
synchronized (assertionLock) {
classAssertionStatus = new HashMap<>();
packageAssertionStatus = new HashMap<>();
defaultAssertionStatus = false;
}
}
/**
* 如果在调用此方法时指定的类被初始化,返回该类将被分配的断言状态。
* 如果已设置命名类的断言状态,则返回最新的设置;否则,如果任何包默认的断言状态与该类相关,则返回最特定相关包默认断言状态的最新设置;否则,返回此类加载器的默认断言状态。
*
* @param className
* 正在查询其所需断言状态的类的完全限定类名。
*
* @return 指定类的所需断言状态。
*
* @since 1.4
*/
boolean desiredAssertionStatus(String className) {
synchronized (assertionLock) {
// assert classAssertionStatus != null;
// assert packageAssertionStatus != null;
// 检查类条目
Boolean result = classAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
// 检查最具体包条目
int dotIndex = className.lastIndexOf(".");
if (dotIndex < 0) { // 默认包
result = packageAssertionStatus.get(null);
if (result != null)
return result.booleanValue();
}
while(dotIndex > 0) {
className = className.substring(0, dotIndex);
result = packageAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
dotIndex = className.lastIndexOf(".", dotIndex-1);
}
// 返回类加载器默认值
return defaultAssertionStatus;
}
}
// 使用VM提供的信息设置断言。
// 注意:只能在同步块内调用
private void initializeJavaAssertionMaps() {
// assert Thread.holdsLock(assertionLock);
classAssertionStatus = new HashMap<>();
packageAssertionStatus = new HashMap<>();
AssertionStatusDirectives directives = retrieveDirectives();
for(int i = 0; i < directives.classes.length; i++)
classAssertionStatus.put(directives.classes[i],
directives.classEnabled[i]);
for(int i = 0; i < directives.packages.length; i++)
packageAssertionStatus.put(directives.packages[i],
directives.packageEnabled[i]);
defaultAssertionStatus = directives.deflt;
}
// 从VM检索断言指令。
private static native AssertionStatusDirectives retrieveDirectives();
}
class SystemClassLoaderAction
implements PrivilegedExceptionAction<ClassLoader> {
private ClassLoader parent;
SystemClassLoaderAction(ClassLoader parent) {
this.parent = parent;
}
public ClassLoader run() throws Exception {
String cls = System.getProperty("java.system.class.loader");
if (cls == null) {
return parent;
}
Constructor<?> ctor = Class.forName(cls, true, parent)
.getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
ClassLoader sys = (ClassLoader) ctor.newInstance(
new Object[] { parent });
Thread.currentThread().setContextClassLoader(sys);
return sys;
}
}
booleanValue();
}
while(dotIndex > 0) {
className = className.substring(0, dotIndex);
result = packageAssertionStatus.get(className);
if (result != null)
return result.booleanValue();
dotIndex = className.lastIndexOf(".", dotIndex-1);
}
// 返回类加载器默认值
return defaultAssertionStatus;
}
}
// 使用VM提供的信息设置断言。
// 注意:只能在同步块内调用
private void initializeJavaAssertionMaps() {
// assert Thread.holdsLock(assertionLock);
classAssertionStatus = new HashMap<>();
packageAssertionStatus = new HashMap<>();
AssertionStatusDirectives directives = retrieveDirectives();
for(int i = 0; i < directives.classes.length; i++)
classAssertionStatus.put(directives.classes[i],
directives.classEnabled[i]);
for(int i = 0; i < directives.packages.length; i++)
packageAssertionStatus.put(directives.packages[i],
directives.packageEnabled[i]);
defaultAssertionStatus = directives.deflt;
}
// 从VM检索断言指令。
private static native AssertionStatusDirectives retrieveDirectives();
}
class SystemClassLoaderAction
implements PrivilegedExceptionAction<ClassLoader> {
private ClassLoader parent;
SystemClassLoaderAction(ClassLoader parent) {
this.parent = parent;
}
public ClassLoader run() throws Exception {
String cls = System.getProperty("java.system.class.loader");
if (cls == null) {
return parent;
}
Constructor<?> ctor = Class.forName(cls, true, parent)
.getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
ClassLoader sys = (ClassLoader) ctor.newInstance(
new Object[] { parent });
Thread.currentThread().setContextClassLoader(sys);
return sys;
}
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)