专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
芋道源码  ·  SpringBoot3.0新特性尝鲜,秒启动 ... ·  昨天  
芋道源码  ·  李飞飞团队50美元训练出DeepSeek R1? ·  昨天  
芋道源码  ·  MySQL数据实时同步到Elasticsea ... ·  3 天前  
芋道源码  ·  某公司新招了个牛逼的架构师后... ·  3 天前  
51好读  ›  专栏  ›  ImportNew

Java 基础中一些值得聊的话题( 加载篇 )

ImportNew  · 公众号  · Java  · 2017-05-03 12:05

正文

(点击 上方公众号 ,可快速关注)


来源:笨狐狸,

blog.csdn.net/liweisnake/article/details/17842877

如有好文章投稿,请点击 → 这里了解详情


在开始Java的类加载旅程之前,可以先参考 这里 了解一些类加载器在Tomcat中的应用(http://blog.csdn.net/liweisnake/article/details/8470285)。


在最初执行java这个命令时,便会调用 ClassLoader 的 getSystemClassLoader 方法显式或者隐式加载 main 方法所在的类及其所引用的类。getSystemClassLoader 会返回 AppClassLoader,后者是 URLClassLoader 的一个子类。


先有鸡还是先有蛋?


所以,最初的一个问题是: 先有鸡还是先有蛋 ?因为 ClassLoader 的整套体系是打包在 jre/lib/rt.jar 中的。只有 rt.jar 先被加载进来,才能够加载别的类;但是 rt.jar 又是被谁加载的呢?自然就是大名鼎鼎的 BootstrapClassLoader。它就是“鸡”。所以严格来讲,BootStrapClassLoader 并不是整个体系中的一部分(可以用 -Xbootclasspath 指定bootstrap 加载的位置)。


当 rt.jar 被加载进来后,ClassLoader 会调用 getSystemClassLoader,其中最重要的一步就是初始化 Launcher、ExtClassLoader 以及AppClassLoader,另外就是将 ContextClassLoader 设为 AppClassLoader。ExtClassLoader 与 AppClassLoader 都是 URLClassLoader 的子类,分别会加载 java.ext.dirs 和 java.class.path 路径下的 jar资源,前者一般指向 jre/lib/ext 下的所有jar,后者就是我们经常念叨的classpath。区分这两个 ClassLoader 的主要目的是,让他们形成层级关系,ExtClassLoader 为 AppClassLoader 的父 ClassLoader,有了层级关系,便可随意使用双亲委托模型了。


ClassLoader extcl;

try {

extcl = ExtClassLoader.getExtClassLoader();

} catch (IOException e) {

throw new InternalError(

"Could not create extension class loader");

}

// Now create the class loader to use to launch the application

try {

loader = AppClassLoader.getAppClassLoader(extcl);

} catch (IOException e) {

throw new InternalError(

"Could not create application class loader");

}

// Also set the context class loader for the primordial thread.

Thread.currentThread().setContextClassLoader(loader);


ClassLoader究竟干了什么?


接下来一个比较重要的问题是ClassLoader究竟干了什么?通常我们只知道它加载了一个类进了jvm,但是具体做了什么呢?


Java设计者把classloader加载一个类的过程分为4步:


  • 第一步,从某个地方得到我们想要的字节码二进制流;

  • 第二步,读入字节码流并转化为Class;

  • 第三步,链接;

  • 第四步,初始化。


其中,第二步一般比较固定,因此ClassLoader提供了defineClass来完成这步;


protected final Class> defineClass(String name, byte[] b, int off, int len,

ProtectionDomain protectionDomain)

throws ClassFormatError

{

protectionDomain = preDefineClass(name, protectionDomain);

Class c = null;

String source = defineClassSourceLocation(protectionDomain);

try {

c = defineClass1(name, b, off, len, protectionDomain, source);

} catch (ClassFormatError cfe) {

c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,

source);

}

postDefineClass(c, protectionDomain);

return c;

}


而 ClassLoader 提供了另一个方法 findClass 来完成第一步及第二步,即从某个地方读入类的二进制流,然后调用 defineClass 返回 Class


protected Class> findClass(final String name)

throws ClassNotFoundException


ClassLoader提供了resolveClass方法完成第三步链接的工作。


protected final void resolveClass(Class> c)


除非特殊需要,否则 尽量重载 findClass 而不是 loadClass。


loadClass 是 Java 1.0 就存在的类,为了增强可扩展性,将findClass和resolveClass封装到了loadClass中,一般我们只需要定义类的加载路径,因此仅需覆盖findClass。


通常我们显示加载类一般会用到ClassLoader.loadClass、Class.forName,他们的区别见这里。


resolveClass做了什么?


resolveClass最终调用了一个本地方法做link,这里的link主要做了这么几步事情:


  1. 验证Class以确保类装载器格式和行为正确;

  2. 准备后续步骤所需的数据结构;

  3. 解析所引用的其他类。


关于这些内容的具体细节,请参考这里。







请到「今天看啥」查看全文