// 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)
-