java 热加载 文件_什么是热加载--自定义加载器

本文介绍了Java热加载的概念,它是通过类加载器实现的,主要用于开发环境提高效率。文章详细讲解了JVM的BootStrap、Extension和Application三大类加载器及其双亲委派机制。接着,作者创建了一个自定义类加载器,覆盖`findClass()`方法,实现在不重启项目的情况下更新代码。通过实例展示了如何使用自定义加载器进行热加载,并指出在实践中需要注意的细节。
摘要由CSDN通过智能技术生成

一、什么是热加载

简单来说,就是不重启一个项目,使得部分代码更新,原理是通过java类加载器实现的。由于类的加载缺少监控使得安全性不能得到保证,一般使用在开发环境,加快开发效率。俗称开发者模式。实现自定义加载器前,先讲一讲JVM的类加载器。

二、JVM的加载器

1、BootStrap ClassLoader

BootStrap ClassLoader又叫启动类加载器,是最顶级的父类加载器,加载最核心的类,,如java.lang.、java.uti.等; 这些类位于$JAVA_HOME/jre/lib/rt.jar;

2、Extension ClassLoader

Extension ClassLoader 又叫扩展类加载器,负责加载%JAVA_HOME%/jre/lib/ext/目录下的扩展包

3、Application ClassLoader

Application ClassLoader又叫应用类加载器,负责加载classpath也就是环境变量下的类

类加载机制

工作原理:类加载采用双亲委派机制,简单来说,一个应用被加载时首先判断是否被加载过,若无则进行加载,并判断是否有父类,若有则要求父类进行加载,到顶级父类时再根据包的位置进行加载,不满足则要求子类加载。

好处:可以避免重复加载,也可以保证类加载的安全性,若自己写了个String包,那是否会替换jdk包中的String类呢?显然不可能,因为使用了这种机制来避免这种问题。

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// 首先判断该类是否已经被加载过

Class> c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

//尝试让父类加载

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

if (c == null) {

//自定义加载,findClass是个空方法就是让我们重写的

long t1 = System.nanoTime();

c = findClass(name);

// this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

JVM是如何判断一个类是否被加载过的呢?通过findLoadedClass(name),即 java虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。否则,返回null。

三、自定义加载器

首先新建一个类并继承ClassLoader。并重写findClass()方法。注释已写方法用处。

public class MyClassLoader extends ClassLoader {

@Override

//通过名称找到具体class文件,并进行加载

protected Class> findClass(String name) {

byte[] b = loadClassData(name);

name = name.split("\\.")[1];

//然后通过defineClass()将二进制文件转化为Class对象

return super.defineClass(name, b, 0, b.length);

}

//这个方法就是将具体位置的class文件转化为二进制流

private byte[] loadClassData(String name) {

try {

name = name.replace(".", "\\");

FileInputStream fileInputStream = new FileInputStream(new File("D:\\IntelliJ IDEA 2019.1\\JavaHotFix\\" + name+".class"));

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

int i = 0;

while ((i = fileInputStream.read()) != -1) {

byteArrayOutputStream.write(i);

}

fileInputStream.close();

return byteArrayOutputStream.toByteArray();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

return null;

}

}

随便定义一个对象并定义一个方法

public class People {

public void hello() {

System.out.println("我是People");

}

}

进行测试

public class Main {

public static void main(String[] args) {

while (true) {

try {

MyClassLoader myClassLoader = new MyClassLoader();

Class> clazz = myClassLoader.loadClass("src.People");

Object people = clazz.newInstance();

clazz.getMethod("hello").invoke(people);

Thread.sleep(2000);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

}

}

}

启动后:

由于就在本级目录,先进行

d589ea68f088

启动后,改变People的hello()方法再重新一遍javac

便得到

d589ea68f088

项目结构:

d589ea68f088

小bug:由于一开始直接name直接写People导致反复不能热加载,后打断点才发现,这个加载器根本没使用到,原来限定名为People导致直接在classpath就被加载了,也就是被父类AppClassLoader加载了。

谢谢大家,觉得不错的兄弟可以把不错打在公屏上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值