美文网首页js css html
Java ClassLoader 基础

Java ClassLoader 基础

作者: Tinyspot | 来源:发表于2023-01-08 20:29 被阅读0次

    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
    

    相关文章

      网友评论

        本文标题:Java ClassLoader 基础

        本文链接:https://www.haomeiwen.com/subject/mrsycdtx.html