一、实现的需求

当一个java被修改后,系统无需重启,替换的类立即生效,这里以Work.java为例

二、实现的思路

1)难点分析

  1. 当项目在执行的时候,我们修改了Work.java,这个类的class文件是不会重新编译的,解决方法?

当Work.java内容发生改变时,调用JavaCompiler对文件进行编译,编译完成后会在Work.java文件所在目录会生成一个Work.class文件,用这个Work.class文件替换编译后目录中的Work.class文件,然后将Work.java目录中的Work.class文件删除,然后就实现了热编译。

  1. 怎么实现文件监听?监听那些文件相关事件?

我这里是从整个项目来进行监听的,方便以后进行拓展,而不是监听单个文件。

引入http://common.io包,继承FileAlterationListenerAdaptor,重写方法。

  1. 怎么实现类的热加载?

自定义类加载器,重写loadClass方法,打破双亲委派机制实现热加载。

2)具体实现

  1. 创建一个线程监听.java文件目录

    1. 当文件内容发生改变时,就重新编译文件
    2. 当Work.class文件创建时,就将编译目录的Work.class替换为这个,然后删除,实现热编
  2. 创建另外一个线程监听.class文件目录
    1. 当文件内容发生改变时就重新加载类
    2. 使用自定义的类进行加载

三、代码实现

  1. class文件目录文件监听类
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;public class FileWatch extends FileAlterationListenerAdaptor {@Overridepublic void onFileCreate(File file) {System.out.println(file.getName()+" created.");File clazz = new File("D:workdatazclassloaderJAVA8targetclassescome");File[] files = clazz.listFiles();for (File f:files) {if(f.isDirectory()){File[] listFiles = f.listFiles();for (File c:listFiles) {convertFile(file,c);}}else{convertFile(file,f);}}}private void convertFile(File file,File f){if(file.getName().equals(f.getName())){try {FileUtils.copyFile(file,f);file.delete();System.out.println("文件转移成功");} catch (IOException e) {e.printStackTrace();System.out.println("文件迁移失败!");}}}@Overridepublic void onDirectoryCreate(File directory) {System.out.println("directory-create:"+directory.getName());}@Overridepublic void onDirectoryChange(File directory) {System.out.println("directory-change:"+directory.getName());}@Overridepublic void onDirectoryDelete(File directory) {System.out.println("directory-delete:"+directory.getName());}@Overridepublic void onFileDelete(File file) {System.out.println(file.getName()+"delete.");}@Overridepublic void onFileChange(File file) {System.out.println(file.getName()+" changed.");JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();int run = compiler.run(null, null, null, file.getAbsolutePath());System.out.println(run == 0 ? "编译成功!" : "编译失败!");}}

  1. class文件目录文件监听线程类
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;import java.io.File;
import java.util.concurrent.TimeUnit;public class ClassFileWatchThread extends Thread {@Overridepublic void run() {System.out.println(".class文件监听线程启动...");File directory = new File("D:workdatazclassloaderJAVA8targetclassescome");// 轮询间隔 5 秒long interval = TimeUnit.SECONDS.toMillis(1);// 创建一个文件观察器用于处理文件的格式FileAlterationObserver observer = new FileAlterationObserver(directory, null);//设置文件变化监听器observer.addListener(new ClassFileWatch());FileAlterationMonitor monitor = new FileAlterationMonitor(interval,observer);try {monitor.start();} catch (Exception e) {e.printStackTrace();}System.out.println(".class文件监听线程启动成功...");}
}

  1. java文件目录文件监听类
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;public class FileWatch extends FileAlterationListenerAdaptor {@Overridepublic void onFileCreate(File file) {System.out.println(file.getName()+" created.");File clazz = new File("D:workdatazclassloaderJAVA8targetclassescome");File[] files = clazz.listFiles();for (File f:files) {if(f.isDirectory()){File[] listFiles = f.listFiles();for (File c:listFiles) {convertFile(file,c);}}else{convertFile(file,f);}}}private void convertFile(File file,File f){if(file.getName().equals(f.getName())){try {FileUtils.copyFile(file,f);file.delete();System.out.println("文件转移成功");} catch (IOException e) {e.printStackTrace();System.out.println("文件迁移失败!");}}}@Overridepublic void onDirectoryCreate(File directory) {System.out.println("directory-create:"+directory.getName());}@Overridepublic void onDirectoryChange(File directory) {System.out.println("directory-change:"+directory.getName());}@Overridepublic void onDirectoryDelete(File directory) {System.out.println("directory-delete:"+directory.getName());}@Overridepublic void onFileDelete(File file) {System.out.println(file.getName()+"delete.");}@Overridepublic void onFileChange(File file) {System.out.println(file.getName()+" changed.");JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();int run = compiler.run(null, null, null, file.getAbsolutePath());System.out.println(run == 0 ? "编译成功!" : "编译失败!");}}

  1. java文件目录文件监听线程类
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;import java.io.File;
import java.util.concurrent.TimeUnit;public class FileWatchThread extends Thread {@Overridepublic void run() {System.out.println(".java文件监听线程启动....");File directory = new File("D:workdatazclassloaderJAVA8srcmainjavacome");// 轮询间隔 5 秒long interval = TimeUnit.SECONDS.toMillis(1);// 创建一个文件观察器用于处理文件的格式FileAlterationObserver observer = new FileAlterationObserver(directory, null);//设置文件变化监听器observer.addListener(new FileWatch());FileAlterationMonitor monitor = new FileAlterationMonitor(interval,observer);try {monitor.start();} catch (Exception e) {e.printStackTrace();}System.out.println(".java文件监听线程启动成功...");}
}

  1. 自定义类加载器(这个类应该在项目最外层)
import java.io.*;public class MyClassLoader extends ClassLoader {private static final String EXT = ".class";private String path;public MyClassLoader() {path = this.getResource("").getPath();}public MyClassLoader(String path) {this.path = path;}// 打破双亲模式,保证自己的类会被自己的classloader加载@Overrideprotected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {byte[] b = null;try {b = loadClassFile(name);} catch (FileNotFoundException e) {System.err.println("加载器" + this.getClass().getName()+ "没有找到class文件" + name + ",将委派给父类加载器!");// 委派给父类加载器return getClass().getClassLoader().loadClass(name);}catch (IOException e) {System.err.println("加载器" + this.getClass().getName() + "加载class文件"+ name + "失败,将委派给父类加载器!");// 委派给父类加载器return getClass().getClassLoader().loadClass(name);}// 检查该类是否被当前类加载器加载过(只检查当前类加载器,不会检查父类加载器)Class<?> clazz = findLoadedClass(name);if (clazz != null) {System.out.println("类" + name + "已被加载过!");return clazz;} else {System.out.println("类" + name + "尚未被加载!");}return this.defineClass(name, b, 0, b.length);}private byte[] loadClassFile(String name) throws IOException,FileNotFoundException {String classFile = getClassFile(name);System.out.println("即将加载class文件" + classFile);ByteArrayOutputStream out = new ByteArrayOutputStream();InputStream input = new FileInputStream(classFile);int count;byte[] temp = new byte[1024];while ((count = input.read(temp)) > -1) {out.write(temp, 0, count);}out.close();input.close();return out.toByteArray();}private String getClassFile(String name) {String pathName = name.replace(".", File.separator);if (path.endsWith("/") || path.endsWith("")) {return path + pathName + EXT;}return path + File.separator + pathName + EXT;}
}

  1. 演示类
public class Worker {public void run(){System.out.println("hello world 1288");}
}

  1. 应用启动类
import come.classloader.ClassFileWatchThread;
import come.file.FileWatchThread;public class App {public static void main(String[] args) throws Exception {//启动监听线程FileWatchThread watchThread = new FileWatchThread();watchThread.start();ClassFileWatchThread classFileWatchThread = new ClassFileWatchThread();classFileWatchThread.start();}
}

四、结果演示

.java文件监听线程启动....
.class文件监听线程启动...
.class文件监听线程启动成功...
.java文件监听线程启动成功...
Worker.java changed.
编译成功!
Worker.class created.
文件转移成功
Worker.class change.
D:workdatazclassloaderJAVA8targetclassescomeWorker.class: Worker
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/comeWorker.class
类come.Worker尚未被加载!
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangObject.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangSystem.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javaioPrintStream.class
hello world
加载器come.MyClassLoader没有找到class文件java.lang.Object,将委派给父类加载器!
加载器come.MyClassLoader没有找到class文件java.lang.System,将委派给父类加载器!
加载器come.MyClassLoader没有找到class文件java.io.PrintStream,将委派给父类加载器!
Worker.classdelete.
Worker.java changed.
编译成功!
Worker.class created.
文件转移成功
加载器come.MyClassLoader没有找到class文件java.lang.Object,将委派给父类加载器!
加载器come.MyClassLoader没有找到class文件java.lang.System,将委派给父类加载器!
加载器come.MyClassLoader没有找到class文件java.io.PrintStream,将委派给父类加载器!
Worker.class change.
D:workdatazclassloaderJAVA8targetclassescomeWorker.class: Worker
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/comeWorker.class
类come.Worker尚未被加载!
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangObject.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangSystem.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javaioPrintStream.class
我的第一个热加载程序实现了!
Worker.classdelete.
Worker.java changed.
编译成功!
Worker.class created.
文件转移成功
加载器come.MyClassLoader没有找到class文件java.lang.Object,将委派给父类加载器!
Worker.class change.
加载器come.MyClassLoader没有找到class文件java.lang.System,将委派给父类加载器!
D:workdatazclassloaderJAVA8targetclassescomeWorker.class: Worker
加载器come.MyClassLoader没有找到class文件java.io.PrintStream,将委派给父类加载器!
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/comeWorker.class
类come.Worker尚未被加载!
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangObject.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javalangSystem.class
即将加载class文件/D:/workdata/zclassloader/JAVA8/target/classes/javaioPrintStream.class
hello world 666666
Worker.classdelete.

从结果来看,项目没有重启,修改了Work.java类,其方法执行的输出值也发生了改变。

自我推荐

这个是我的微信公众号,欢迎扫码关注,谢谢!

查看类的实现类mac_自定义类加载器实现热加载相关推荐

  1. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 )

    文章目录 一.使用 DexClassLoader 获取组件类失败报错 二.失败原因分析 一.使用 DexClassLoader 获取组件类失败报错 在上一篇博客 [Android 逆向]启动 DEX ...

  3. 16.不同类加载器作用与加载动作分析---(获取ClassLoader的途径)

    1. 打印类加载器 2. 通过应用类加载器来获取指定当前项目的文件的全路径 因为应用类加载器可以加载当前应用的类,所以我们可以通过它的getResource方法来获取这个类的全路径 package c ...

  4. Android进阶:十三、自定义类加载器加载加密类文件

    之前面试的时候有许多面试官问类加载器相关的问题,所以这是一个很重要的知识点.而且对于高级Android研发来讲,懂得更多类加载相关的东西,对开发也会有很多的帮助,比如热更新,类加密等. 其实笔者对类加 ...

  5. 类加载器源码、双亲委派、自定义类加载器详解

    文章目录 jdk的类加载器 双亲委派 自定义类加载器 打破双亲委派 jdk的类加载器 查看一个类的类加载器: ClassLoader classLoader = boy.class.getClassL ...

  6. java的类加载器以及如何自定义类加载器

    ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的. 类加载器结构 结构:BootstrapClassLoader(祖父)–>ExtClassLoader ...

  7. JVM类加载机制、双亲委派机制、自定义类加载器、打破双亲委派机制

    1.类加载器 站在Java虚拟机的角度看,只有两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现(HotSpot虚拟机.JDK8中), ...

  8. 类加载机制、双亲委派机制深度解析以及如何自定义类加载器

    文章目录 1.类加载运行的全过程 2. JVM类加载器的初始化 3.双亲委派机制 4.编写自定义类加载器 5.(Tomcat)如何打破双亲委派机制 当我们运行一个类的时候,首先要通过类加载机制把类加载 ...

  9. JVM自定义类加载器在代码扩展性的实践

    一.背景 名单管理系统是手机上各个模块将需要管控的应用配置到文件中,然后下发到手机上进行应用管控的系统,比如各个应用的耗电量管控:各个模块的管控应用文件考虑到安全问题,有自己的不同的加密方式,按照以往 ...

最新文章

  1. @2021高考生,用 Python 分析专业“钱景”
  2. 基于HTML5 Canvas 实现矢量工控风机叶轮旋转
  3. java基础面试题整理-2021
  4. ACR2010_MTX单药治疗临床疗效良好但放射学进展的早期RA患者的预测因素和临床意义...
  5. app inventor调用图像识别_+AI场景,3步懂图像识别产品
  6. mongodb 导入 mysql_将mongodb 数据指定字段导出,然后指定字段导入mysql 实例 及相关问题解决...
  7. artcore html5,值得收藏的25款免费响应式网页模板_CSS_网页制作
  8. 为什么买入不了创业版_趋势波段买卖创业板ETF指数基金能轻松保险盈利
  9. ORA-12541 TNS:无监听程序问题解决
  10. MFC中打开一个获取路径的对话框
  11. 【Python学习实践教程】10个Python经典项目实战,练手必备
  12. Qt Http下载器
  13. 千方百剂显示服务器错误,千方百剂远程服务器地址
  14. Elastic控制查询精准度-minimum_should_match
  15. im开源java框架_开源的im即时通讯系统
  16. 禁用wordpress新编辑器,使用经典编辑器的方法
  17. 清理win7系统盘空间
  18. Django中的Model(字段) - 第五轻柔的code - 博客园
  19. 关于FIN_WAIT1
  20. 百度一下,你就知道”你真的知道嘛?

热门文章

  1. jquery 获取tr里的值_jquery获取tr中控件值并操作tr实现思路
  2. java 本地内存_哪个更快:Java堆还是本地内存
  3. coredump gdb 调试_CRASH安装和调试
  4. oracle 统计文本字符串,oracle人员权限,字符串转列,统计管理详解-Oracle
  5. mysql mvc javascript_MVC中用Jquery、JS和Ajax 实现分页 存储过程是用mysql写的。
  6. 电信充q币短信怎么发_移动、联通、电信话费快来领!微信小额提现免手续费方法!刚需羊毛!...
  7. 爬虫demo_全自动爬虫,你爱了么!解放双手的时刻
  8. 玩转 SpringBoot 2 之整合定时任务篇
  9. Cron 触发器及相关内容 (第一部分)
  10. 核心对象+持久对象全析(1)