• 《Java 底层原理》Java 类加载器


    前言

    工作中需要实现一种功能:动态加载类对象信息,简单说就是class变了Jvm能够立马知道并且加载到内存。

    类加载器分类

    Java类加载器分为两种,一种是加载启动类,另一种是其他类加载器。Java加载类的关系:Launcher

    1. 启动类加载器(BootstrapClassLoader)。

    Java 程序是运行在Jvm上,所以Jvm需要知道Java程序的入口在何处(启动类)。Jvm通过c++的LoadMainClass去加载Java程序的sun.launcher.LauncherHelper。并且通过他加载Mian函数所在的扩展类ClassLOader和AppClassLoader。

    2. Java自己编写的类加载器。

    2.1 ExtClassLoader 扩展类加载器

    ExtClassLoader类是没有父类的。

    String var0 = System.getProperty("java.ext.dirs");

    因为上面的代码,所以可以设置类扩展类加载器加载Jar包的目录。

    2.2 AppClassLoader Class_path 指定的Jar包

    AppClassLoader的父类为ExtClassLoader。我们main函数所在类的类加载器是AppClassLoader。

    Java类加载器会加载的Jar包路径

    import sun.misc.Launcher;
    
    import java.net.URL;
    
    public class ClassLoaderDemo1 {
        public static void main(String[] args) {
            String[] split = System.getProperty("sun.boot.class.path").split(";");
            for (int i = 0; i < split.length; i++) {
                System.out.println(split[i]);
            }
            System.out.println("========================================");
            URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
            for (int i = 0; i < urLs.length; i++) {
                System.out.println(urLs[i]);
            }
        }
    }

    运行结果:

    委派机制

    根据类加载器,画一个Java委派和加载的原理图。

    Java不会被重新加载是通过类的全限定名来判断的

    Java委派机制的好处。

    1. 保证核心包加载不受干扰。

    2.  保证class 加载唯一。

    SPI机制

    先看个案例

    public interface ServerSPI {
        void execMethod();
    }

    两个实现类

    public class ServerSPIImpl1 implements ServerSPI{
        @Override
        public void execMethod() {
            System.out.println("ServerSPIImpl1.execMethod 执行");
        }
    }

    public class ServerSPIImpl2 implements ServerSPI {
        @Override
        public void execMethod() {
            System.out.println("ServerSPIImpl2.execMethod 执行");
        }
    }

    配置(目录要一致,文件名是接口名)

    文件内容(类的全限定名)

    写一个测试类

    import sun.misc.Service;
    import java.util.Iterator;
    
    public class SpiTest {
        public static void main(String[] args) {
            Iterator<ServerSPI> providers = Service.providers(ServerSPI.class);
    
            while(providers.hasNext()) {
                ServerSPI ser = providers.next();
                ser.execMethod();
            }
        }
    }

    运行结果:

    ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);   -- 这个方式也可以获取加载的对象。

    该案例没有使用Java的委派机制实现了类的加载功能。

    Service 的源码重点:

    private static final String prefix = "META-INF/services/";    -- 为什么目录是指定的。
        public static <S> Iterator<S> providers(Class<S> var0) throws ServiceConfigurationError {
            ClassLoader var1 = Thread.currentThread().getContextClassLoader();     -- 获取线程所在个类ClassLoader。
            return providers(var0, var1);
        }

    SPI加载类的原理:

    1. 需要指定目录,程序需要在知道目录下找到接口信息,根据接口找到实现类。

    2. 通过线程所在的ClassLoader 加载实现,通过反射实例化对象。

    3. 其他细节源码,有兴趣自己了解。

    4. 我们经常使用的JDBC就是使用SPI机制实现类加载。

    自定义类加载器

    实现热部署功能案例:https://www.cnblogs.com/jssj/p/13251804.html

    案例中很好的运用了自定义类加载器,并且实现了如何打破Java委派机制。

    总结

    类加载器的种类,类加载器的各自分工,类加载的委派过程,SPI机制,自定义加载器的实现。

  • 相关阅读:
    关联本地代码的方式 HTTPS和SSH---Gitee码云
    详解elementUI表单的验证规则---vue(基本用法)
    vscode 设置缩进 4
    vue的图片懒加载
    A complete log of this run can be found in问题解决
    简单直观的搞懂Vue3的ref、reactive、toRef、toRefs
    vue-cli3.0 引入外部字体并使用
    迅为与龙芯强强联合匠心之作iTOP-2K1000开发板正式发布
    迅为RK3399开发板外接固态硬盘测试
    迅为i.MX6Q开发板Ubuntu20.04 Can通信
  • 原文地址:https://www.cnblogs.com/jssj/p/14285530.html
Copyright © 2020-2023  润新知