簡單來講,就是不重啓一個項目,使得部分代碼更新,原理是經過java類加載器實現的。因爲類的加載缺乏監控使得安全性不能獲得保證,通常使用在開發環境,加快開發效率。俗稱開發者模式。實現自定義加載器前,先講一講JVM的類加載器。java
BootStrap ClassLoader又叫啓動類加載器,是最頂級的父類加載器,加載最核心的類,,如java.lang.*、java.uti.*等; 這些類位於$JAVA_HOME/jre/lib/rt.jar;安全
Extension ClassLoader 又叫擴展類加載器,負責加載%JAVA_HOME%/jre/lib/ext/目錄下的擴展包ide
Application ClassLoader又叫應用類加載器,負責加載classpath也就是環境變量下的類測試
工做原理:類加載採用雙親委派機制,簡單來講,一個應用被加載時首先判斷是否被加載過,若無則進行加載,並判斷是否有父類,如有則要求父類進行加載,到頂級父類時再根據包的位置進行加載,不知足則要求子類加載。this
好處:能夠避免重複加載,也能夠保證類加載的安全性,若本身寫了個String包,那是否會替換jdk包中的String類呢?顯然不可能,由於使用了這種機制來避免這種問題。spa
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。3d
首先新建一個類並繼承ClassLoader。並重寫findClass()方法。註釋已寫方法用處。code
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;
}
複製代碼
} 隨便定義一個對象並定義一個方法cdn
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();
}
}
}
}
複製代碼
啓動後: 因爲就在本級目錄,先進行
啓動後,改變People的hello()方法再從新一遍javac 便獲得 項目結構:小bug:因爲一開始直接name直接寫People致使反覆不能熱加載,後打斷點才發現,這個加載器根本沒使用到,原來限定名爲People致使直接在classpath就被加載了,也就是被父類AppClassLoader加載了。