敞开成长之旅!这是我参与「日新计划 12 月更文应战」的第 13 天,点击查看活动概况

日拱一卒无有尽 功不唐捐终入海

欢迎关注大众号【架构染色】交流学习

一、布景

之前蒋老师看到《当月亮守护地球 | SkyWalking Agent 守护你的应用…有它相伴才安逸》时,有对其中这行文字小有酌量。

类加载具有传递性:如当需求加载一个类时,JVM 默认会运用(当时需求运用此类的)调用者 Class 对象的 ClassLoader 来加载此类;举例:类 A 中 import 类 B,那么类 A 的 ClassLoader 会去加载类 B。

上边这段文字看起来有点微妙,传递性好像第一次听说,这也导致了蒋老师随之表示,接下来要全面的学习ClassLoader(嗯,大众号又有高质量文章能够转载了)。

本篇是对ClassLoader中 类的隐式加载和显式加载做个直观的介绍和验证。这个知识点笔者个人认为很重要,屡次帮助笔者处理日常工作中遇到的疑难杂症,如果你尚未认真研究过ClassLoader,但懵懂的认知让你觉得这个应该很简单,那请看下图,若看不懂则标明你可能并不了解ClassLoader中得一些要害逻辑;不贩卖焦虑,不感兴趣则疏忽,知道个大概即可,不会它并不影响你做一个优异的程序员

ClassLoader:类的隐式加载和显式加载

类加载要害逻辑图示(来自网络).png

二、问题与答案

1) 问题

通常咱们都知道咱们写的类,会被 JVM 加载,那都怎样被加载呢?

2)答案

  1. 类最常见的办法是经过import的办法引进的,那么 JVM 自动帮咱们去加载。

  2. 别的一种状况是咱们指定类加载器去加载,如运用 Class#forName(xxx)或者 ClassLoader#loadClass(xxx)

三、什么是自动加载

  1. 验证咱们的代码中被import导入的类,在运用的时分会被当时类加载器自动加载,便是运用当时类加载器loadClass办法。
  2. 当时类加载器怎样了解, 当时类被哪个类加载器加载的,那么这个类加载器就叫做这个类的当时类加载器。那么这个类中经过import办法引进了其他类,就被 JVM 自动的运用当时类加载器的loadClass办法加载。

这个很好验证,ClassLoader 的 loadClass 办法里自己打个断点,调试一下。

四、什么对错自动加载

非自动加载便是咱们指定一个类加器去加载类,根据需求从forNameloadClass中二选一。

问自己一个问题,forName是不是要遵守双亲派遣模型?双亲派遣的代码逻辑在 ClassLoader#loadClass 中,那是不是 forName 还会调用 ClassLoader 的 loadClass 的办法逻辑呢?答案是:会的,经过一个比如验证一下 。

1)示例准备

  • 接口定义

    public interface MsgCenter {
        public boolean sendMsg(String msg);
    }
    
  • 接口完成:

    ```
    public class MsgValidater {
        public boolean validate(String msg){
            return  true;
        }
    }
    public class MsgCenterImpl implements MsgCenter {
        @Override
        public boolean sendMsg(String msg) {
            // import MsgValidater类型
            return new MsgValidater().validate(msg);
        }
    }
    ```
    

2)编列测试计划

测试的要害逻辑如下:

  1. 先经过appClassLoader 加载接口
  2. 再经过子加载器以forName办法加载完成类
  3. 完成类实例化后并赋值给接口;调用sendMsg办法,此办法内引用了MsgValidater类。

逻辑看似杂乱,只要略微耐心一点也没什么,无非是自动加载和自动加载的组合再加上双亲派遣的机制进行了一场混沌验证;笔者个人的经验是,学习这类知识最好的办法是重复调试并做好小笔记,用思维导图整理自己的认知模型,感兴趣的话还是要调试一下,不感兴趣就别再看了。

/**
 * 把MsgCenterImpl.class 移动到子加载器的加载途径中.保证只要子加载器可加载.
 */
@Test
public void testImportLoadClass(){
    CustomClassLoader01 customClassLoader01 = new CustomClassLoader01(ClassLoader.getSystemClassLoader());
    try {
        //appClassloader 加载 接口
        MsgCenter msgCenter = null;
        //子加载器,加载 完成类
        Class<?> aClass = Class.forName("com.rock.MsgCenterImpl",false,customClassLoader01);
        //赋值 接口 = 完成类 ;这是能够的,因为子加载器可见父加载器所加载的类.
        msgCenter =(MsgCenter)aClass.newInstance();
        boolean hello = msgCenter.sendMsg("hello");
        System.out.println("after sendMsg");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
}

3)结果契合预期

CustomClassLoader start  load class :com.rock.MsgCenterImpl  ; resolve : false
CustomClassLoader findLoadedClass : null
CustomClassLoader getParent().loadClass(name) : ClassNotFoundException
CustomClassLoader loadClassData : com.rock.MsgCenterImpl
//以上信息阐明:forName办法经过自定义类加载器的loadClass办法加载了com.rock.MsgCenterImpl
CustomClassLoader start  load class :com.rock.MsgCenter  ; resolve : false
CustomClassLoader findLoadedClass : null
CustomClassLoader start  load class :java.lang.Object  ; resolve : false
CustomClassLoader findLoadedClass : null
//以上信息阐明:子加载器 委托父加载器加载 com.rock.MsgCenter ,java.lang.Object
CustomClassLoader start  load class :com.rock.classLoader.MsgValidater  ; resolve : false
CustomClassLoader findLoadedClass : null
CustomClassLoader getParent().loadClass(name) : ClassNotFoundException
CustomClassLoader loadClassData : com.rock.classLoader.MsgValidater
//以上信息阐明:自定义加载器加载了com.rock.classLoader.MsgValidater
after sendMsg
//以上信息阐明,完成类赋值给接口后,调用接口的办法履行成功.

参阅并感谢

blog.csdn.net/WeiPeng2K/a…