美文网首页RxJavaJava服务器端编程编程语言爱好者
自定义SPI和热部署技术破坏类加载器的双亲委派模式

自定义SPI和热部署技术破坏类加载器的双亲委派模式

作者: 迦叶_金色的人生_荣耀而又辉煌 | 来源:发表于2020-12-08 07:20 被阅读0次

上一篇 <<<ClassLoader类加载器顺序Demo测试与双亲委派源码解读
下一篇 >>>JVM中对象如何完成空间分配和初始化工作


1.SPI破解类加载器的双亲委派模式

Java SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。
实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制.

a、项目结构图

b、演示效果

        //1.不设置,或设置当前线程的类加载器为APP加载器,则load正常读取
//        Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader());
        //2.设置当前线程的加载类为扩展加载,则load读取不到,因为扫不到包
//        Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader().getParent());
        //3.设置当前线程的加载类为启动类,则load可正常读取
//        Thread.currentThread().setContextClassLoader(SPITest.class.getClassLoader().getParent().getParent());
//
        ServiceLoader<JaryeService> load = ServiceLoader.load(JaryeService.class);
        Iterator<JaryeService> iterator = load.iterator();
        while (iterator.hasNext()) {
            JaryeService next = iterator.next();
            System.out.println(next + "\t" + next.getName());
        }

c、源码解析

从源码中可以看到使用此方式不存在先执行parent的情况,即可破解了双亲委派模式。



数据库连接方式的底层实现也是此方式:
Connection root = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=UTF-8","root", "root");

DriverManager的初始化方法:loadInitialDrivers()
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();

2.自定义类加载器的方式破坏双亲委派模式(热部署)

a、继承ClassLoader
b、重写findClass方法,其中调用父类的defineClass方法

public class JaryeClassLoader extends ClassLoader {
    /**
     * 重写findClass方法
     * @param name
     * @return
     */
    @Override
    public Class<?> findClass(String name) {
        try {
            byte[] data = getClassFileBytes(this.fileObject);
            return defineClass(name, data, 0, data.length);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
public class ManualHotDeployment {
    private static long startTime;

    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 通过自定义类加载器,读取信息
        JaryeClassLoader jaryeClassLoader = new JaryeClassLoader();
        jaryeClassLoader.setFileObject(new File("/Users/jiangjinrong/Downloads/OrderServiceImpl.class"));
        Class<?> aClass = jaryeClassLoader.findClass("com.jarye.classloader.service.OrderServiceImpl");
        OrderService orderService = (OrderService) aClass.newInstance();
        System.out.println("读取class成功:" + orderService.addOrder());
        File fileObject = jaryeClassLoader.getFileObject();
        startTime = fileObject.lastModified();
        // 定时轮询读取信息并通过类加载器更新
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {

                }
                long endTime = fileObject.lastModified();
                if (endTime != startTime) {

                    System.out.println("class文件发生了变化,需要重新读取");
                    try {
                        JaryeClassLoader newMayiktClassLoader = new JaryeClassLoader();
                        newMayiktClassLoader.setFileObject(new File("/Users/jiangjinrong/Downloads/OrderServiceImpl.class"));
                        Class<?> newClass = newMayiktClassLoader.findClass("com.jarye.classloader.service.OrderServiceImpl");
                        OrderService newOrderService = (OrderService) newClass.newInstance();
                        System.out.println("读取class成功:" + newOrderService.addOrder());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    startTime = endTime;
                }
            }
        }).start();
    }
}

热部署与热加载的区别

热部署的案例:SpringBoot、tomcat、jetty

相同点:
a.不重启服务器编译/部署项目
b.基于Java的类加载器实现

不同点:
a、部署方式
热部署在服务器运行时重新部署项目
热加载在运行时重新加载class
b、实现原理
热部署直接重新加载整个应用
热加载在运行时重新加载class
c、使用场景
热部署更多的是在生产环境使用
热加载则更多的实在开发环境使用


相关文章链接:
<<<JVM整体内存结构的图解,直观明了
<<<javap命令查看对象信息及操作方法在JVM层的实现原理
<<<javap命令反查汇编指令汇总
<<<ClassLoader类加载器顺序Demo测试与双亲委派源码解读
<<<JVM中对象如何完成空间分配和初始化工作
<<<JVM元空间(方法区)和栈内存溢出原因及解决方案
<<<JVM堆内存溢出和内存泄露问题定位和解决
<<<JVM常见死锁问题产生原因和多种诊断方式
<<<服务器CPU飙升为100%问题排查及如何避免
<<<JVM内存诊断命令和排查工具汇总
<<<JVM新生代老年代算法汇总图解
<<<JVM垃圾回收不要手动System.gc的真正原因
<<<JVM垃圾回收引用计数法和根搜索算法图解
<<<JVM垃圾回收STW(Stop-The-World)代码演示
<<<JVM垃圾回收器的发展历程及使用场景汇总
<<<JVM串行并行垃圾回收器的关注点
<<<一张图看懂CMS垃圾回收器的底层原理
<<<G1能作为JDK9默认垃圾回收器的优势分析
<<<CMS和G1的漏标问题解决及三色标记算法图解
<<<GC中新生代进入老年代的方式汇总
<<<GC常用日志参数配置及分析工具说明
<<<FullGC、MinorGC、STW等常见问题如何解答
<<<JVM性能调优的评估指标及调优示例

相关文章

  • JVM中对象如何完成空间分配和初始化工作

    上一篇 <<<自定义SPI和热部署技术破坏类加载器的双亲委派模式[https://www.jianshu.com/...

  • SPI的ClassLoader问题

    问题 为什么说spi服务机制破坏了双亲委派模型? 双亲委派机制 启动类加载器(Bootstrap ClassLoa...

  • 类加载阅读目录

    什么是类加载器 类加载器与类的”相同“判断 类加载器种类 双亲委派模型 类加载过程 自定义类加载器 JAVA热部署...

  • Java 类加载

    双亲委派模型 并非强制 而是推荐 SPI 父类加载器需要子类加载器加载类 打破双亲委派模型 https://www...

  • 自定义SPI和热部署技术破坏类加载器的双亲委派模式

    上一篇 <<

  • 3、双亲委派模型详解

    3. 双亲委派模型   双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双...

  • java类的加载机制

    概念 类的生命周期 类加载器 jvm类加载机制 类的加载方式 双亲委派模式

  • 双亲委派模型与自定义类加载器

    双亲委派模型与自定义类加载器 其实,双亲委派模型并不复杂。自定义类加载器也不难!随便从网上搜一下就能搜出一大把结果...

  • 四、初识ClassLoader

    双亲委派的定义 类加载器的父亲委托机制(双亲委派机制) 类加载器加载一个类时,会优先交给其父的加载器加载,父加载器...

  • Java虚拟机

    JVM 组成部分 类加载器 执行引擎 内存区 本地方法调用 类加载器 双亲委派模型 类的加载过程采用双亲委派机制,...

网友评论

    本文标题:自定义SPI和热部署技术破坏类加载器的双亲委派模式

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