Java-API简析_java.lang.ClassLoader类(基于 Latest JDK)(浅析源码)

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://blog.csdn.net/m0_69908381/article/details/131345825
出自【进步*于辰的博客

启发博文:《一看你就懂,超详细java中的ClassLoader详解》(转发)。
参考笔记三,P57.3、P58.4。

注:依赖类:ClassSecurityManager

文章目录

1、概述

继承关系:

  • java.lang.Object
    • java.lang.ClassLoader

直接已知子类:
SecureClassLoader


public abstract class ClassLoader extends Object

类加载器是负责加载类的对象。ClassLoader 类是一个抽象类。如果给定类的二进制名称(见下文),那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。

每个 Class 对象都包含一个对定义它的 ClassLoader 的引用

数组类的 Class 对象不是由类加载器创建的,而是由 Java 运行时根据需要自动创建。数组类的类加载器由 Class.getClassLoader() 返回,该加载器与其元素类型的类加载器是相同的;如果该元素类型是基本类型,则该数组类没有类加载器

应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机 动态加载类 \color{purple}{动态加载类} 动态加载类的方式。

类加载器通常由 安全管理器 \color{blue}{安全管理器} 安全管理器使用,用于指示 安全域 \color{green}{安全域} 安全域

ClassLoader 类使用 委托模型 \color{brown}{委托模型} 委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 “bootstrap class loader”)本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。

通常情况下,Java 虚拟机以与平台有关的方式,从本地文件系统中加载类。例如,在 UNIX 系统中,虚拟机从 CLASSPATH 环境变量定义的目录中加载类。

然而,有些类可能并非源自一个文件;它们可能源自其他来源(如网络),也可能是由应用程序构造的。defineClass() 将一个字节数组转换为 Class 类的实例。这种新定义的类的实例可以使用 Class.newInstance() 来创建( 反射 \color{red}{反射} 反射) 。

类加载器所创建对象的方法和构造方法可以引用其他类。为了确定引用的类,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) {
        // load the class data from the connection
         . . .
    }
}

二进制名称
按照《Java Language Specification》的定义,任何作为 String 类型参数传递给 ClassLoader 中方法的类名称都必须是一个二进制名称。
有效类名称的示例包括:

"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"

从以下版本开始:
1.0
另请参见:
resolveClass(Class)

2、构造方法摘要

属性说明:

private static ClassLoader scl;// 系统类加载器
private static boolean sclSet;// 是否已 set 系统类加载器
private final ClassLoader parent;// 父类加载器

private static String usr_paths[];
private static String sys_paths[];

2.1 protected null

使用方法 getSystemClassLoader() 返回的 ClassLoader 创建一个新的类加载器,将该加载器作为父类加载器。

protectedClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}

getSystemClassLoader()见第3.19项,this()见5.1项。

底层逻辑:检查调用线程是否有权创建新类加载器,若通过,使用系统类加载器构造子类加载器。

2.2 protected ClassLoader parent

使用指定的、用于委托操作的父类加载器创建新的类加载器。

protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}

this()见5.1项。

3、方法摘要

3.1 void clearAssertionStatus()

将此类加载器的默认断言状态设置为 false,并放弃与此类加载器关联的所有默认包或类断言状态设置。

3.2 protected Class<?> defineClass(byte[] b, int off, int len)

已过时。defineClass(String, byte[], int, int) 取代

示例:
1、java源文件。
在这里插入图片描述
2、测试。

File f = new File("C:\\Users\\于辰\\Downloads\\新建文件夹\\Test.class");
FileInputStream fis = new FileInputStream(f);
int len;
byte[] bs = new byte[10];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = fis.read(bs)) != -1) {
    baos.write(bs, 0, len);
}

byte[] code = baos.toByteArray();
Class z1 = ClassLoader.class;
Method m1 = z1.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m1.setAccessible(true);

z1 = (Class) m1.invoke(ClassLoader.getSystemClassLoader(), code, 0, code.length);
sout z1;// class com.frank.test.Test
m1 = z1.getMethod("say", null);
m1.invoke(z1.newInstance(), null);// Say Hello

与下1项相比,无需指定name,默认为class字节码文件中定义的全限定名。

3.3 protected Class<?> defineClass(String name, byte[] b, int off, int len)

将一个字节数组转换为 Class 类的实例。

示例:(使用上1项中的 java源文件)

File f = new File("C:\\Users\\于辰\\Downloads\\新建文件夹\\Test.class");
FileInputStream fis = new FileInputStream(f);
int len;
byte[] bs = new byte[10];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((len = fis.read(bs)) != -1) {
    baos.write(bs, 0, len);
}

byte[] code = baos.toByteArray();
Class z1 = ClassLoader.class;
Method m1 = z1.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
m1.setAccessible(true);

z1 = (Class) m1.invoke(ClassLoader.getSystemClassLoader(), "com.frank.test.Test", code, 0, code.length);
sout z1;// class com.frank.test.Test
m1 = z1.getMethod("say", null);
m1.invoke(z1.newInstance(), null);// Say Hello

name是全限定名,若与class字节码文件中定义的不同,则抛出异常。

3.4 protected Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)

使用可选的 ProtectionDomain 将一个字节数组转换为 Class 类的实例。

3.5 protected Class<?> defineClass(String name, ByteBuffer b, ProtectionDomain protectionDomain)

使用可选的 ProtectionDomain 将 ByteBuffer 转换为 Class 类的实例。

3.6 protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase)

根据 name 在此 ClassLoader 中定义包。

3.7 protected Class<?> findClass(String name)

使用指定的二进制名称查找类。

3.8 protected String findLibrary(String libname)

返回本机库的绝对路径名。

3.9 protected Class<?> findLoadedClass(String name)

如果 Java 虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。

protectedfinal Class<?> findLoadedClass(String name) {
    if (!checkName(name))// 判断是否有效,见第4.7项
        return null;
    return findLoadedClass0(name);
}

private native final Class<?> findLoadedClass0(String name);

3.10 proteced URL findResource(String name)

查找具有给定名称的资源。

3.11 protected Enumeration<URL> findResources(String name)

返回表示所有具有给定名称的资源的 URL 对象的枚举。

3.12 protected Class<?> findSystemClass(String name)

查找具有指定的二进制名称的类,必要时加载它。

3.13 protected Package getPackage(String name)

返回由此类加载器或其任何祖先所定义的 Package。

3.14 protected Package[] getPackages()

返回此类加载器及其祖先所定义的所有 Package。

3.15 ClassLoader getParent()

返回委托的父类加载器。

public final ClassLoader getParent() {
    if (parent == null)
        return null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(parent, Reflection.getCallerClass());
    }
    return parent;
}

主业务:

return parent;

3.16 URL getResource(String name)

查找具有给定名称的资源。

3.17 InputStream getResourceAsStream(String name)

返回读取指定资源的输入流。

3.18 Enumeration<URL> getResources(String name)

查找所有给定名称的资源。

3.19 static ClassLoader getSystemClassLoader()

返回委托的系统类加载器。

public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}

主业务:

initSystemClassLoader();// 初始化系统类加载器,见第 4.2项

return scl;// 返回 AppClassLoader

3.20 static URL getSystemResource(String name)

从用来加载类的搜索路径中查找具有指定名称的资源。

3.21 static InputStream getSystemResourceAsStream(String name)

从用来加载类的搜索路径打开具有指定名称的资源,以读取该资源。

3.22 static Enumeration<URL> getSystemResources(String name)

从用来加载类的搜索路径中查找所有具有指定名称的资源。

3.23 Class<?> loadClass(String name)

使用指定的二进制名称来加载类。

public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);// 调用下1项,暂不知 resolve 何用
}

3.24 protected Class<?> loadClass(String name, boolean resolve)

使用指定的二进制名称来加载类。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
    	// 检查此类是否已加载
        Class<?> c = findLoadedClass(name);// 返回已加载的 Class 实例,见第 9项
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);// 递归
                } else {
                    c = findBootstrapClassOrNull(name);// 见第4.8项
                }
            } catch (ClassNotFoundException e) {
                // 若不为空的父类加载器也未找到,抛出异常
            }

            if (c == null) {
                // 仍未找到,调用findClass()查找
                long t1 = System.nanoTime();
                c = findClass(name);

                // 这是定义的 loader,记录一次状态
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);// 见下1项
        }
        return c;
    }
}

主业务:

// 检查此类是否已加载
Class<?> c = findLoadedClass(name);// 返回已加载的 Class 实例,见第 9项

if (parent != null) {
    c= parent.loadClass(name, false);// 递归
} else {
    c = findBootstrapClassOrNull(name);// 见第4.8项
}

if (c == null) {
    c = findClass(name);// 仍未找到,调用findClass()查找
    // 注:ExtClassLoader 去 java.ext.dirs 路径下查找,AppClassLoader 去 java.class.path 路径下查找
}

这就是启发博文中所说的【双亲委托】。

3.25 protected void resolveClass(Class<?> c)

链接指定的类。

protectedfinal void resolveClass(Class<?> c) {
    resolveClass0(c);
}
private native void resolveClass0(Class<?> c);

暂未知“链接”是何意。

3.26 void setClassAssertionStatus(String className, boolean enabled)

设置在此类加载器及其包含的嵌套类中指定的最高层类所需的断言状态。

3.27 void setDefaultAssertionStatus(boolean enabled)

设置此类加载器的默认断言状态。

3.28 void setPackageAssertionStatus(String packageName, boolean enabled)

为指定包设置默认断言状态。

3.29 protected void setSigners(Class<?> c, Object[] signers)

设置类的签署者。

4、方法摘要(不开放)

4.1 private static Void checkCreateClassLoader()

确认调用线程是否有权创建类加载器。

private static Void checkCreateClassLoader() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkCreateClassLoader();// 检查权限,见第4.7项
    }
    return null;
}

4.2 private static synchronized void initSystemClassLoader()

初始化系统类加载器。

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;
    }
}

主业务:

sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
scl = l.getClassLoader();

return loader;

ExtClassLoader extcl = ExtClassLoader.getExtClassLoader();
loader = AppClassLoader.getAppClassLoader(extcl);

说明:

  1. AppClassLoader 的parent是 ExtClassLoader。
  2. 由 AppClassLoader 为scl赋值。

扩展一点: \color{red}{扩展一点:} 扩展一点:

public ExtClassLoader(File[] var1) throws IOException {
    super(getExtURLs(var1), null, Launcher.factory);
}
public URLClassLoader(URL[] urls, ClassLoader parent,
                      URLStreamHandlerFactory factory) {
    super(parent);
}

ExtClassLoader 无parent

4.3 static void checkClassLoaderPermission(ClassLoader cl, Class<?> caller)

若指定 Class 对象的类加载器是指定类加载器的父类加载器或“超类加载器”,确认调用线程是否有权获取类加载器。

staticvoid checkClassLoaderPermission(ClassLoader cl, Class<?> caller) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        ClassLoader ccl = getClassLoader(caller);// 获取类加载器
        if (needsClassLoaderPermissionCheck(ccl, cl)) {// 见下1项
            sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);// 检查权限,调用第4.18项
        }
    }
}

4.5 private static boolean needsClassLoaderPermissionCheck(ClassLoader from, ClassLoader to)

在“有效”的情况下,判断指定的两个类加载器、前者是否是后者的父类加载器或“超类加载器”。若前者不存在,返回 false

private static boolean needsClassLoaderPermissionCheck(ClassLoader from,
                                                       ClassLoader to)
{
    if (from == to)
        return false;

    if (from == null)
        return false;

    return !to.isAncestor(from);// 见下1项
}

4.6 boolean isAncestor(ClassLoader cl)

判断指定类加载器是否是当前类加载器的父类加载器或“超类加载器”。

boolean isAncestor(ClassLoader cl) {
    ClassLoader acl = this;
    do {// 循环的目的是获取当前类加载器的父类或超类加载器
        acl = acl.parent;
        if (cl == acl) {
            return true;
        }
    } while (acl != null);
    return false;
}

4.7 private boolean checkName(String name)

判断指定名称是否为null或是否可能是一个无效的二进制名称。

private boolean checkName(String name) {
    if ((name == null) || (name.isEmpty()))
        return true;
    if ((name.indexOf('/') != -1)
        || (!VM.allowArraySyntax() && (name.charAt(0) == '[')))
        return false;
    return true;
}

示例:

Class z1 = ClassLoader.class;
Method m1 = z1.getDeclaredMethod("checkName", String.class);
m1.setAccessible(true);
System.out.println(m1.invoke(ClassLoader.getSystemClassLoader(),"java.lang.Object"));// true
System.out.println(m1.invoke(ClassLoader.getSystemClassLoader(),"[java.lang.Object"));// false

暂不知为何规定有效的二进制名称要以']'开头。

4.8 private Class<?> findBootstrapClassOrNull(String name)

使用 BootStrapClassLoader 寻找指定二进制名称的类。

private Class<?> findBootstrapClassOrNull(String name)
{
    if (!checkName(name)) return null;// 判断是否有效,见上1项

    return findBootstrapClass(name);
}

private native Class<?> findBootstrapClass(String name);// 去 sun.boot.class.path 路径中查找,未找到返回 null

5、构造方法摘要(不开放)

5.1 private (Void unused, ClassLoader parent)

在指定的条件下(是否有权),根据指定的父类加载器创建一个新的类加载器。

private ClassLoader(Void unused, ClassLoader parent) {
    this.parent = parent;
    if (ParallelLoaders.isRegistered(this.getClass())) {
        parallelLockMap = new ConcurrentHashMap<>();
        package2certs = new ConcurrentHashMap<>();
        assertionLock = new Object();
    } else {
        // no finer-grained lock; lock on the classloader instance
        parallelLockMap = null;
        package2certs = new Hashtable<>();
        assertionLock = this;
    }
}

后续解析。

最后

如果大家需要Java-API文档,我上传了《Java-API文档-包含5/8/11三个版本》。


本文暂缓更新。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进步·于辰

感谢打赏!很高兴可以帮到你!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值