// 2020-08-01:之前的代碼 findClass 寫成 loadClass 了,弄錯了。
熱部署:
dynamic.ClassLoader:
package dynamic;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
public class ClassLoadStudy {
public static void main(String[] args) throws Exception {
HotDeploy hot = new HotDeploy("dynamic.Task");
hot.monitor();
while (true) {
TimeUnit.SECONDS.sleep(2);
hot.getTask().run();
}
}
}
/**
* 熱部署類
*/
class HotDeploy {
private static volatile Runnable instance;
private final String FILE_NAME;
private final String CLASS_NAME;
public HotDeploy(String name) {
CLASS_NAME = name; // 類的完全限定名
name = name.replaceAll("\\.", "/") + ".class";
FILE_NAME = (getClass().getResource("/") + name).substring(6); // 判斷class文件修改時間使用,substring(6)去掉開頭的file:/
}
/**
* 獲取一個任務
* @return
*/
public Runnable getTask() {
if (instance == null) { // 雙重檢查鎖,單例,線程安全
synchronized (this) {
if (instance == null) {
try {
instance = createTask();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return instance;
}
/**
* 創建一個任務,重新加載 class 文件
*/
private Runnable createTask() {
try {
Class<?> clazz = new MyClassLoader(null).loadClass(CLASS_NAME);
if (clazz != null)
return (Runnable)clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 監視器,監視class文件是否被修改過,如果是的話,則重新加載
*/
public void monitor() {
Thread t = new Thread(()->{
try {
long lastModified = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis();
while(true) {
TimeUnit.SECONDS.sleep(1);
long now = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis();
if(now != lastModified) { // 如果class文件被修改過了
System.out.println("yes");
lastModified = now;
instance = createTask(); // 重新加載
}
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
});
t.setDaemon(true); // 守護進程
t.start();
}
/**
* 自定義類加載器
*/
private class MyClassLoader extends ClassLoader {
private MyClassLoader(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] b = Files.readAllBytes(Path.of(FILE_NAME));
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
}
}
動態改變的 Task 類,dynamic.Task:
package dynamic;
public class Task implements Runnable {
@Override
public void run() {
System.out.print("=> ");
}
}
-
-
URL getClass.getResource(String path) -
InputStream getClass().getResourceAsStream(String path) -
getResource("")返回當前類所在的包的路徑 -
getResource("/")返回當前的 classpath 根據路徑
-
-
path 不能以
/開始,path 是從 classpath 根開始算的, 因為classloader 不是用戶自定義的類,所以沒有相對路徑的配置文件可以獲取,所以默認都是從哪個classpath 路徑下讀取,自然就沒有必要以/開頭了 。-
URL Class.getClassLoader().getResource(String path) -
InputStream Class.getClassLoader().getResourceAsStream(String path)
-


