首页 > 编程语言 >java类加载中的双亲委派机制

java类加载中的双亲委派机制

时间:2024-11-14 17:32:47浏览次数:1  
标签:java 自定义 ClassLoader 应用程序 Cat 双亲 加载

 双亲委派机制的优点: 同样的类不会被重复加载。

 

一、概述:
类加载器:类加载器用于加载  .class字节码文件到内存中(并为每个 .class字节码文件生成  Class对象)。

 

二、四种类加载器介绍:

​编辑

   2.1.启动类加载器(Bootstrap ClassLoader):  用于加载jdk的核心类类库中的类,具体加载( jdk/jre/lib)  jdk目录/jre子目录/lib子目录中的类(java官方类库) 、或sun. boot.class.path属性指定的 路径下的类(了解即可)。 还可以加载扩展类加载器应用程序类加载器。 但通常处于安全性考虑只允许其加载 java、sun、javax包及子包下的类(比如  java.lang.Math,    java.lang.String)。 

 

  2.2.扩展类加载器 Extension ClassLoader:

用于加载:  Java 语言编写,由 sun.misc.Launcher$ExtClassLoader实现。其继承了 java.lang.ClassLoader抽象类。 父加载器为启动类加载器(Bootstrap ClassLoader)。

用于加载Java的扩展类库,   jdk/jre/lib/ext目录中的 JAR 压缩文件和类。

 

2.3.应用程序类加载器  ApplicationClassLoader:

  Java 语言编写,由 sun.misc.Launcher$AppClassLoader 实现。其继承了 java.lang.ClassLoader抽象类。 父加载器为扩展类加载器。用于加载:自己编写的类、第三方库提供的类、一些其他的资源文件。是java开发的应用程序默认的类加载器,一般来说,Java 开发的应用程序中的类都是由它来完成加载。

注意:应用程序类加载器  ApplicationClassLoader也叫做系统类加载器, 因为是项目默认的类加载器。

 

2.4.自定义类加载器:

日常开发中,上述3种类加载器相互配合就几乎可以加载所有的类及相关资源。 但必要时,可以自定义类加载器,来定制类的加载方式。  

注意:  如果一个项目中用户没有创建并启动自定义类加载器,则本项目中就不涉及用户自定义类加载器

 

public class CustomClassLoader extends ClassLoader {
    //省略类自定义加载器的其他具体实现代码。


    public static void main(String[] args) {
        try {
            // 创建自定义的类加载器1
            CustomClassLoader loader1 = new CustomClassLoader();
            Class<?> clazz1 = loader1.findClass("com.test.java.User");


            // 创建自定义的类加载器2
            CustomClassLoader loader2 = new CustomClassLoader();
            Class<?> clazz2 = loader2.findClass("com.test.java.User");


            // clazz1和clazz2对应了不同的类模版结构
            System.out.println(clazz1 == clazz2);           
            // false
            System.out.println(clazz1.getClassLoader());    
            // com.test.java.CustomClassLoader@4aa298b7
            System.out.println(clazz2.getClassLoader());    
            // com.test.java.CustomClassLoader@28d93b30


        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

  

由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会在子加载器中重复加载。但是注意,类加载器“邻居”间,同一类型仍然可以被加载多次,因为互相并不可见。(如上面的 loader1 和 loader2可以把User类加载两次)

自定义类加载器的优点:

1.修改类加载的方式:  可以根据实际情况在某个时间点按需进行动态加载(延迟加载)。

2.扩展加载源:比如从数据库、网络、甚至是电视机机顶盒进行加载。

3.防止源码泄漏:Java代码容易被编译和篡改,可以进行编译加密。那么可以用自定义类加载器,解密字节码文件。

 

四种类加载器总结:

   2.4.1.父子关系:

  • 1.启动类加载器没有父加载器, 子加载器是扩展类加载器。
  • 2.扩展类加载器的子加载器是应用程序类加载器。
  • 3.应用程序类加载器的子加载器是用户自定义类加载器
  • 4.用户自定义类加载器没有子加载器。

 2.4.2.加载路径:

  • 启动类加载器,加载: java运行环境中的类 (jre中的类):   jdk\jre\lib中的类及资源
  • 扩展类加载器,加载:  java运行环境中的扩展路径下的类 (jre中ext子路径下的类及资源):   jdk\jre\lib\ext 子路径中的类及资源   (比启动类加载器加载的范围更小)
  • 应用程序类加载器,加载: 自己开发的项目下的类及资源。
  • 自定义类加载器,加载: 软件工程师指定的路径下的类及资源。

  

 

三、双亲委派机制下的类加载流程:

​编辑

上图中列出了三种类加载器加载类的过程(1.应用程序类加载器AppClassLoader, 2.扩展类加载器ExtClassLoader、 3. 引导类加载器Bootstrap ClassLoader)  (没有自定义类加载器: 如果一个项目中用户没有创建并启动自定义类加载器,则本项目中就不涉及用户自定义类加载器)

三种类加载器依据双亲委派机制加载类的具体过程如下:

假设要加载一个自己编写的  day1114.Cat类,则具体过程分为如下两大块(假定没有自定义类加载器):

1.查询day1114.Cat类是否加载过(如果加载过就直接使用,没加载过就将加载的需求委派给父加载器加载)

  • 应用程序类加载器 ApplicationClassLoader查询是否加载过day1114.Cat类如果加载过就直接使用,没加载过就将加载的需求委派给父加载器加载(扩展类加载器Extension ClassLoader)
  • 扩展类加载器Extension ClassLoader查询是否加载过day1114.Cat类如果加载过就直接使用,没加载过就将加载的需求委派给父加载器加载(启动类加载器Bootstrap ClassLoader)
  • 启动类加载器Bootstrap ClassLoader查询是否加载过day1114.Cat类如果加载过就直接使用,没加载过就尝试加载day1114.Cat类。能加载就加载并使用,如果无法加载就将加载请求委派子加载类加载。

2.加载day1114.Cat类:

  •   如果启动类加载器没加载过就尝试加载day1114.Cat类。能加载就加载并使用并结束加载Cat类的过程(子类加载器就不会再加载这个Cat类了),如果无法加载就将加载请求委派子加载类加载(扩展类加载器Extension ClassLoader)
  • 扩展类加载器Extension ClassLoader尝试加载day1114.Cat类。能加载就加载并使用并结束加载Cat类的过程(子类加载器就不会再加载这个Cat类了),如果无法加载就将加载请求委派子加载类加载(应用程序类加载器Application ClassLoader)
  • 应用程序类加载器Application ClassLoader尝试加载day1114.Cat类。能加载就加载并使用并结束加载Cat类的过程(子类加载器就不会再加载这个Cat类了),如果无法加载就将加载请求委派子加载类加载(自定义类加载器User ClassLoader), 但如果项目中没有创建自定义类加载器 则会抛出ClassNotFoundException的异常。

 

优点: 同样的类不会被重复加载(避免类的重复加载,确保一个类的全局唯一性,保护程序安全,防止核心 API 被随意篡改)。

由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会在子加载器中重复加载。但是注意,类加载器“邻居”间,同一类型仍然可以被加载多次,因为互相并不可见。(如下面的 loader1 和 loader2)

举例说明:

  假设自己定义了一个   java.lang.String类   (命名空间<包名和类名>和java官方编写的java.lang.String一样,类中的代码不一样)。假设没有双亲委派机制, 则类的加载过程:

1.java.lang.String类由应用程序类加载器(Application ClassLoader)加载

注意:在java内存中,同一命名空间中,不会出现全限定名相同的两个类

2. 启动类加载器则 不会再加载 /jre/lib/rt.jar 下的 java.lang.String

则核心类 java.lang.String 被篡改成了自定义的String类。

 

四、案例测试:

用代码查看三种类加载器

 

 public static void main(String[] args) {
        // 应用程序类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader); 
       // sun.misc.Launcher$AppClassLoader@18b4aac2


        // 扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader); 
        // sun.misc.Launcher$ExtClassLoader@232204a1


        // 引导类加载器
        ClassLoader bootstrapClassLoader = 
            extClassLoader.getParent();
        System.out.println(bootstrapClassLoader); // null (因为是C++编写的所以为null)
}

 

4.1.引导类加载器:

 

    public static void main(String[] args) {
        //获取引导类加载器加载的类库
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : urLs) {
            System.out.println(urL);
        }


        // 从上边的路径中随意选择一个类,查看其类加载器
        ClassLoader classLoader1 = Object.class.getClassLoader();
        System.out.println(classLoader1); // null


    }
    //运行结果: 有两种,一个是jdk1.8.0_77/jre/lib下的jar, 一种是null
    //null并不是说明没有引导类加载器,而是因为引导类加载器是用C/C++语言实现的
    //file:/C:/Program Files/Java/jdk1.8.0_77/jre/lib/  下的很多jar文件
    //null

 

4.2.扩展类加载器:

可以看到扩展类加载器加载的是  jdk\jre\lib\ext中的资源   (另一个加载路径 windows\sun\java\lib\ext了解即可)

 

public static void main(String[] args) {
        // 扩展类加载器加载的类库
        String extDirs = System.getProperty("java.ext.dirs");
        for (String extPath : extDirs.split(";")) {
            System.out.println(extPath);
        }
        //C:\Program Files\Java\jdk1.8.0_77\jre\lib\ext
        //C:\Windows\Sun\Java\lib\ext
        


        // 从上边的路径中随意选择一个类,查看其类加载器
        ClassLoader classLoader2 = CurveDB.class.getClassLoader();
        System.out.println(classLoader2);
        //sun.misc.Launcher$ExtClassLoader@4b67cf4d
    }

 

 

4.3.应用程序类加载器:

查看系统默认的应用程序类加载器,和自己定义的类采用的类加载器  (都是ApplicationClassLoader)

 

public static void main(String[] args) {
        // 获取应用程序类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2


        // 对于自定义的类,查看其类加载器
        ClassLoader classLoader3 = Cat.class.getClassLoader();
        System.out.println(classLoader3); 
        //sun.misc.Launcher$AppClassLoader@18b4aac2
 }

 

 

 

 

参考文章:

Java类加载器-CSDN博客https://blog.csdn.net/scj1022/article/details/135054087

 

 


 

author : zhaoyq    2024-11-14

标签:java,自定义,ClassLoader,应用程序,Cat,双亲,加载
From: https://www.cnblogs.com/zhaoyongqi/p/18546462

相关文章