热加载 hotswap-classloader
简介
什么是 hotswap-classloader
?
hotswap-classloader
是由笔者 litongjava 开发的一款 Java 动态类加载器。其核心功能是在 Java 应用运行时动态地更换或更新类定义,而无需重启整个 JVM。这种热替换(Hot Swapping)能力对于开发过程中的快速迭代和测试尤为有用,因为它显著减少了等待应用重启的时间。
为什么将 hotswap-classloader
与 tio-boot
结合使用?
将 hotswap-classloader
与 tio-boot
结合使用,可以为 Java 网络应用开发带来以下关键优势:
- 快速迭代和测试:通过使用
hotswap-classloader
,开发者可以在不重启服务器的情况下实时更新类文件,实现快速迭代和即时测试。 - 提高开发效率:减少了重启应用程序所需的时间,开发者可以更加专注于代码的编写和改进,从而提高工作效率。
- 支持敏捷开发:在敏捷开发模式下,频繁的更改和测试是常态。
hotswap-classloader
的动态加载能力使得这一过程更加流畅和高效。
总的来说,结合使用 hotswap-classloader
和 tio-boot
不仅提高了开发效率,而且增强了网络应用开发的灵活性和便利性。这对于希望快速迭代和改进其网络应用的开发团队来说,是一个非常有价值的组合。
整合热加载
整合热加载的步骤
添加依赖
在项目的
pom.xml
文件中添加以下依赖:<dependency> <groupId>com.litongjava</groupId> <artifactId>hotswap-classloader</artifactId> <!-- https://central.sonatype.com/artifact/com.litongjava/hotswap-classloader --> <version>${hotswap-classloader.version}</version> </dependency>
使用
TioApplicationWrapper
启动服务在启动类中使用
TioApplicationWrapper
来启动应用:import com.litongjava.hotswap.wrapper.tio.boot.TioApplicationWrapper; import com.litongjava.jfinal.aop.annotation.AComponentScan; @AComponentScan public class HelloApp { public static void main(String[] args) { long start = System.currentTimeMillis(); TioApplicationWrapper.run(HelloApp.class, args); long end = System.currentTimeMillis(); System.out.println((end - start) + "ms"); } }
启动热加载
- 在启动类的程序参数(Program arguments)中添加启动参数
--mode=dev
,或者在配置文件中添加对应的配置。 TioApplicationWrapper
会自动判断是否启用热加载。判断逻辑是:如果 ClassLoader 的 URL 中包含/target/classes/
,则是开发环境,开启热加载;否则不是生产环境,不开启
- 在启动类的程序参数(Program arguments)中添加启动参数
测试热加载效果
- 在 Eclipse IDE 中:保存文件即可触发热加载,实时查看效果。
- 在 IDEA 环境中:需要在运行时手动编译(
Build
->Recompile
)文件才能看到效果。
日志示例
```log
2024-09-15 13:48:09.531 [main] INFO c.l.h.w.t.b.TioApplicationWrapper.runDev:75 - start hotswap watcher:Thread[HotSwapWatcher,10,main]
2024-09-15 13:48:09.540 [main] INFO c.l.h.w.t.b.TioApplicationWrapper.runDev:82 - new hotswap class loader:com.litongjava.hotswap.classloader.HotSwapClassLoader@7cf10a6f
2024-09-15 13:48:09.578 [main] INFO c.l.t.u.e.EnvUtils.load:171 - app.env:null
2024-09-15 13:48:09.611 [main] INFO c.l.j.a.s.ComponentScanner.findClasses:72 - resource:file:/E:/code/java/project-litongjava/tio-boot-web-hello/target/classes/com/litongjava/tio/web/hello
2024-09-15 13:48:09.671 [main] INFO c.l.t.u.Threads.getTioExecutor:93 - new worker thead pool:com.litongjava.tio.utils.thread.pool.SynThreadPoolExecutor@612679d6[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
2024-09-15 13:48:09.676 [main] INFO c.l.t.u.Threads.getGroupExecutor:51 - new group thead pool:java.util.concurrent.ThreadPoolExecutor@52f759d7[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
2024-09-15 13:48:09.698 [main] INFO c.l.t.b.c.TioApplicationContext.run:278 - init:57(ms),scan class:17(ms),config:50(ms),server:13(ms),http route:16(ms)
2024-09-15 13:48:09.698 [main] INFO c.l.t.b.c.TioApplicationContext.printUrl:295 - port:80
http://localhost
363ms
2024-09-15 13:48:46.892 [pool-1-thread-1] INFO c.l.h.w.HotSwapWatcher.process:156 - watch event ENTRY_MODIFY,IndexController.class
2024-09-15 13:48:46.893 [pool-1-thread-1] INFO c.l.h.w.HotSwapWatcher.process:165 - restart server:com.litongjava.hotswap.wrapper.tio.boot.TioBootRestartServer@126afc98
loading
2024-09-15 13:48:46.893 [pool-1-thread-1] INFO c.l.t.b.c.TioApplicationContext.close:317 - stop server
2024-09-15 13:48:46.894 [tio-group-2] INFO c.l.t.s.AcceptCompletionHandler.failed:132 - The server will be shut down and no new requests will be accepted:0.0.0.0:80
2024-09-15 13:48:46.895 [pool-1-thread-1] INFO c.l.t.u.Threads.close:177 - shutdown group thead pool:java.util.concurrent.ThreadPoolExecutor@52f759d7[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 7]
2024-09-15 13:48:46.896 [pool-1-thread-1] INFO c.l.t.u.Threads.close:188 - shutdown worker thead pool:com.litongjava.tio.utils.thread.pool.SynThreadPoolExecutor@612679d6[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 2]
2024-09-15 13:48:46.896 [pool-1-thread-1] INFO c.l.t.s.TioServer.stop:141 - 0.0.0.0:80 stopped
2024-09-15 13:48:46.898 [pool-1-thread-1] INFO c.l.h.w.t.b.TioBootRestartServer.restart:33 - new classLoader:com.litongjava.hotswap.classloader.HotSwapClassLoader@5ff60e5f
2024-09-15 13:48:46.904 [pool-1-thread-1] INFO c.l.t.u.e.EnvUtils.load:171 - app.env:null
2024-09-15 13:48:46.904 [pool-1-thread-1] INFO c.l.j.a.s.ComponentScanner.findClasses:72 - resource:file:/E:/code/java/project-litongjava/tio-boot-web-hello/target/classes/com/litongjava/tio/web/hello
2024-09-15 13:48:46.912 [pool-1-thread-1] INFO c.l.t.u.Threads.getTioExecutor:93 - new worker thead pool:com.litongjava.tio.utils.thread.pool.SynThreadPoolExecutor@6bb1b24e[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
2024-09-15 13:48:46.913 [pool-1-thread-1] INFO c.l.t.u.Threads.getGroupExecutor:51 - new group thead pool:java.util.concurrent.ThreadPoolExecutor@6c0d059c[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
2024-09-15 13:48:46.917 [pool-1-thread-1] INFO c.l.t.b.c.TioApplicationContext.run:278 - init:5(ms),scan class:3(ms),config:5(ms),server:1(ms),http route:4(ms)
2024-09-15 13:48:46.917 [pool-1-thread-1] INFO c.l.t.b.c.TioApplicationContext.printUrl:295 - port:80
http://localhost
Loading complete in 24 ms (^_^)
热加载的实现原理
1. 动态类加载
hotswap-classloader
通过自定义 ClassLoader,实现了在运行时动态加载和替换类的功能。当检测到 class 文件发生变化时,创建一个新的 ClassLoader 来加载更新后的类定义,并使用反射机制重新初始化应用上下文。
2. 文件监控
使用 Java NIO 的 WatchService 监控 class 文件的变动。当检测到文件修改事件时,触发热加载流程。
3. 自定义线程池
由于 NIO2 的默认线程池不支持热加载,为了避免线程池中的线程持有旧的 ClassLoader 导致类无法卸载的问题,hotswap-classloader
使用了自定义的线程池 tio-group
。在热加载时,旧的线程池会被安全地关闭,新的线程池会随新的 ClassLoader 一起创建。
4. 生产环境优化
在生产环境中,TioApplicationWrapper
会自动检测运行模式,避免加载热加载相关的功能,从而不引入额外的性能损耗。生产环境中,仍然使用 NIO 的默认线程池,确保性能和稳定性。
更多使用说明
有关热加载的更多使用细节和高级配置,请参考官方文档:
https://github.com/litongjava/hotswap-classloader
部署注意事项
- 禁用热加载:在部署到生产环境时,不要在程序参数中添加
--mode=dev
。TioApplicationWrapper
会调用TioApplication
来启动项目,不会启用热加载功能。 - 性能考虑:理论上,生产环境不会有多余的性能损耗,因为
TioApplicationWrapper
仅在启动时判断了运行模式和 classpath 路径。
总结
通过整合 hotswap-classloader
,开发者可以在不重启应用的情况下实时更新代码,大大提高了开发效率。同时,得益于对生产环境的优化处理,热加载机制不会对线上应用的性能和稳定性造成影响。
如果是 Eclipse IDE,保持一个文件即可测试加载效果,如果是 IDEA 环境需要再运行时手动编译(Build-->Recompile)文件才可以看到效果
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。