热部署:
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:/ } // 获取一个任务 public Runnable getTask() { if (instance == null) { // 双重检查锁,单例,线程安全 synchronized (HotDeploy.class) { if (instance == null) { try { instance = createTask(); } catch (Exception e) { e.printStackTrace(); } } } } return instance; } // 创建一个任务,重新加载 class 文件 private Runnable createTask() { try { Class clazz = MyClassLoader.getLoader().loadClass(CLASS_NAME); if (clazz != null) return (Runnable)clazz.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } // 监视器,监视class文件是否被修改过,如果是的话,则重新加载 public void monitor() throws IOException { Thread t = new Thread(()->{ try { long lastModified = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis(); while(true) { Thread.sleep(500); long now = Files.getLastModifiedTime(Path.of(FILE_NAME)).toMillis(); if(now != lastModified) { // 如果class文件被修改过了 lastModified = now; instance = createTask(); // 重新加载 } } } catch (InterruptedException | IOException e) { e.printStackTrace(); } }); t.setDaemon(true); // 守护进程 t.start(); } } // 自定义的类加载器 class MyClassLoader extends ClassLoader { @Override public Class<?> findClass(String name) throws ClassNotFoundException { try { String fileName = "/" + name.replaceAll("\\.", "/") + ".class"; InputStream is = getClass().getResourceAsStream(fileName); byte[] b = is.readAllBytes(); return defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } public static MyClassLoader getLoader() { return new MyClassLoader(); } }
-
-
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)
-