Java热加载(一)—— ClassLoader实现热加载

前面的文章我们一直在讨论类加载器相关的知识,这篇文章我们谈一下,类加载器的实际应用热加载。说到热加载,做过线上项目的一定用过,下面我们来讨论一下热加载。

我们将讨论常见的两种形式的热加载:一种使用ClassLoader实现热加载的方式;另一种使用Instrumentation的方式。本文我们一起讨论ClassLoader如何实现热加载。

ClassLoader实现热加载

通过前面的文章我们知道了类加载机制是双亲委派模型,“同一个类名,同一个类加载器实例加载的,代表是同一个类”。如果我们要自己实现业务代码的热加载,就不能用默认的类加载器实例,因为同一个类加载器实例加载一次后会存起来,后面的class文件就算更新了也不会再次加载了。

我还是通过一个例子来说明

自定义类加载器

loadClass:加载类的入口方法,其中实现遵循了双亲委派机制,该方法返回Class实例;(最好不要破坏双亲委派机制)
findClass:loadClass方法中,如果其父类加载器无法加载指定类,则调用自身的findClass方法完成加载,该方法返回Class实例;
defineClass:接收以字节数组表示的类字节码,并把它转换成 Class 实例;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;

public class MyClassLoader extends ClassLoader{

    // 热加载文件的存放路径
    private String classpath;

    MyClassLoader(String classpath){
        super(ClassLoader.getSystemClassLoader());
        this.classpath = classpath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            return null;
        }
        // defineClass:接收以字节数组表示的类字节码,并把它转换成Class实例
        return defineClass(null, classData, 0, classData.length);
    }

    private byte[] loadClassData(String name) {
        try {
            name = name.replaceAll("\\.", "//");
            String path = classpath + name + ".class";
            File file = new File(path);
            if(!file.exists()) {
                return null;
            }
            // 将class文件转化成字节数组
            FileInputStream in = new FileInputStream(file);
            ByteArrayOutputStream bao = new ByteArrayOutputStream();
            int b = 0;
            while ((b = in.read()) != -1) {
                bao.write(b);
            }
            in.close();
            return bao.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

这样我们自定义的类加载器就完成了,下面我们就可以进行测试了

定义热加载的类

public interface TestClass {
    void test();
}

public class TestClassImpl implements TestClass {
    @Override
    public void test() {
        System.out.println("after !!!!!");
    }
}

public class Test {

    private static final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    public static void main(String[] args) {
        scheduledExecutorService.scheduleAtFixedRate(Test::classLoaderTest, 1, 1, TimeUnit.SECONDS);
    }

    private static void classLoaderTest() {
        try {
            MyClassLoader myClassLoader = new MyClassLoader("D:/pch/");
            Class<?> clazz = myClassLoader.findClass("com.pch.reload.TestClassImpl");
            System.out.println("加载TestClassImpl热加载成功");
            TestClass testClass = (TestClass) clazz.getConstructor().newInstance();
            testClass.test();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这样我们就能实现了热加载,例子比较简单,实际应用中,可以通过对外提供一个统一的调用,获得类实现。

下篇文章我们将要介绍一下Instrumentation方式实现热加载,这种方式也是我们项目中实际使用的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值