Java ClassLoader 基础

1. 类加载

  • JVM 首次使用某个类时,需通过 ClassPath 查找该类的 .class 文件
  • 将 .class 文件中对类的描述信息加载到内存中,进行保存
  • 加载时机
    • 创建对象
    • 创建子类对象
    • 访问静态属性
    • 调用静态方法
    • 主动加载:Class.forName("full-name")

1.1 class 文件

包名、类名、父类、属性、方法、构造方法.....

2. 类加载器

  • 在运行期间,如果我们要产生某个类的对象,JVM 会检测该类型的 Class 对象是否已被加载; 如果没有加载,JVM 会根据类的名称找到 .class 文件并加载它
  • Class 对象代表 Java 应用程序在运行时所加载的类或接口实例,每加载一个类,JVM自动生成一个Class对象

2.1 ClassLoader的分类

  • Bootstrap ClassLoader 启动类加载器(引导类加载器)
  • ExtClassLoader 扩展类加载器(Java9 之后改为 Platform Classloader)
  • Application Classloader(系统类加载器或应用类加载器)默认的类加载器
  • 自定义类加载器,父类加载器为AppClassLoader

2.2 ClassLoader 层次结构

  • 系统类加载器 --父--> 扩展类加载器 --父--> 引导类加载器
  • 除了引导类加载器之外,所有的类加载器都有一个父类加载器。 通过 getParent()方法可以得到
  • 注意:父加载器不是父类

2.3 类与类加载器

  • 在JVM中表示两个class对象是否为同一个类对象的两个必要条件
    • 类的全限定名必须一致
    • 加载这个类的ClassLoader必须相同
  • 在JVM中,即使这个两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的

2.3 获取 ClassLoader

@Test
public void getClassLoader() {
    Class<?> clazz = String.class;
    ClassLoader classLoader = clazz.getClassLoader();
    System.out.println(classLoader);
    // null, 根加载器并不是由Java语言实现的,因此拿不到根加载器对象
}
@Test
public void getClassLoader2() throws ClassNotFoundException {
    // this.getClass().getClassLoader();
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Class<?> clazz = classLoader.loadClass("com.example.concrete.common.domain.User");
    System.out.println(classLoader);
    System.out.println(classLoader.getParent());
}

打印结果

sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@51efea79

2.4 获得class对象的三种方法

方式一:对象.getClass()

String str = "hello";
Class<?> clazz = str.getClass();

方式二:类.class
Class<?> clazz = String.class;

方式三:Class. forName() 动态加载类

// 静态方法 forName("类的全限定名")
Class<?> clazz3 = Class.forName("java.lang.String");

3. ClassLoader 分析

  • loadClass(String name)
  • findClass(String name)
  • defineClass(String name, byte[] b, int off, int len)
// ClassLoader 的默认实现就是双亲委托
public abstract class ClassLoader {

  //每个类加载器都有个父加载器
  private final ClassLoader parent;

  public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
  }

  protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
  }

  protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
    return defineClass(name, b, off, len, null);
  }
}
// name: 类的全限定名
public Class<?> loadClass(String name) throws ClassNotFoundException {
    return loadClass(name, false);
}
/**
 * classpath 就是一组目录的集合;classpath 是 JVM 用到的一个环境变量,它用来指示 JVM 如何搜索class
 * 在启动 JVM 时设置 classpath 变量
 * java -cp  or  java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello
 * 如果不设置,JVM 默认的 classpath 为. 即当前目录
 */
String classpath = System.getProperty("java.class.path");
Properties properties = System.getProperties();
Set<String> strings = properties.stringPropertyNames();
for (String string : strings) {
    System.out.println(string);
}
/**
 * 若以“/”开头的,表示要从项目的 ClassPath 开始的( /mapper/xxx.xml ),
 * 如果前面没有这个“/”,那么表示的就是相对于该类的路径继续往下
 */
URL resource = classLoader.getResource("");  // file:/.../target/classes/
URL resource1 = classLoader.getResource("bean.xml");  // file:/.../target/classes/bean.xml

4. Classpath

  • lib 和 classes 同属 classpath,访问优先级为: lib > classes
  • Java 项目 /src 目录下的文件(*.xml, *.properties)编译后会放到 WEB-INF/classes 目录,默认的 classpath 就是 WEB-INF/classes
  • WEB-INF/ 是资源目录,客户端不能直接访问
  • Maven 项目 resources 目录下的文件编译后在 BOOT-INF/classes

4.1 maven 项目 classpath 路径

Maven 项目目录

src
  |-- main
    |-- java
      |-- com.xxx
  |-- resources
    |-- application.yml

编译后目录

target
  |-- classes
    |-- com.xxx
    |-- application.yml

打包的 jar 解压后目录

|-- BOOT-INF
  |-- classes
    |-- com.xxx
    |-- application.yml
  |-- lib
|-- org.springframework.boot.loader...

引用 classpath 路径下的文件,只需在文件名前加 classpath:

classpath:application-*.xml
# 子目录
classpath:config/*.xml
# **/ 表示任意目录
classpath:**/bean.xml

4.2 classpath vs classpath*

  • classpath 在当前classpath 中查找,只加载第一个 classpath 路径
  • classpath* 不仅包含 class 路径, 还包括 jar 文件(classpath目录)
  • classpath* 会从所有的classpath中加载文件
classpath:*.xml
classpath*:config.xml
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容