整合 Hot Reload
Netty-Boot 整合 Hotswap-ClassLoader 实现热加载
简介
在开发过程中,为了提高开发效率,通常会需要实时修改代码后立即生效,而无需重启整个应用程序。hotswap-classloader
提供了一个高效的解决方案,能够在不重启 JVM 的情况下,实现类的动态替换。本文将介绍如何将 hotswap-classloader
整合到 Netty-Boot
项目中,从而实现代码热加载(Hot Reload)功能。
热加载原理
热加载(Hot Reload)是指在应用运行时动态替换已经加载的类,避免重启整个应用程序。其核心在于类加载器的使用。Java
运行时环境通过类加载器 (ClassLoader
) 加载类的字节码,并在运行时生成对应的类对象。
hotswap-classloader
通过自定义的类加载器 HotSwapClassLoader
实现类的热替换。当代码发生变更后,HotSwapClassLoader
会在不影响 JVM 的情况下重新加载修改后的类,替换旧的类对象。这一机制使得应用可以在不重启的情况下更新业务逻辑。
工作流程
- 文件监控:
HotSwapWatcher
线程会持续监听指定目录下的.class
文件变化,一旦检测到文件的改动(如保存或重新编译),会触发热加载过程。 - 重新编译:开发者修改源码并编译后,
.class
文件被更新。 - 类加载替换:
HotSwapClassLoader
会创建一个新的类加载器实例,重新加载变更后的类,并替换应用中正在使用的类。 - 自动重启服务:对于 Netty 服务,
NettyApplicationWrapper
会自动重启服务器并加载新的类。
这样,开发者可以在不重启 Netty 服务的前提下,快速迭代代码并看到即时效果。
整合 Hotswap-ClassLoader
1. 添加依赖
在项目的 pom.xml
文件中添加 hotswap-classloader
的依赖:
<!-- 热加载类加载器 -->
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>hotswap-classloader</artifactId>
<version>${hotswap-classloader.version}</version>
</dependency>
2. 使用 NettyApplicationWrapper
启动项目
通过 NettyApplicationWrapper
来启动 Netty 项目,NettyApplicationWrapper
集成了 hotswap-classloader
,实现了自动监听类文件变化并热加载的功能。
启动类代码如下:
package com.litongjava.study.netty.boot;
import com.litongjava.annotation.AComponentScan;
import com.litongjava.hotswap.wrapper.netty.boot.NettyApplicationWrapper;
@AComponentScan
public class NettyHelloApp {
public static void main(String[] args) {
long start = System.currentTimeMillis();
NettyApplicationWrapper.run(NettyHelloApp.class, args);
long end = System.currentTimeMillis();
System.out.println((end - start) + "(ms)");
}
}
3. 验证热加载
当你看到以下日志时,表示 HotSwapClassLoader
成功加载:
2024-10-01 23:24:17.354 [main] INFO c.l.h.w.n.b.NettyApplicationWrapper.runDev:76 - start hotswap watcher:Thread[HotSwapWatcher,10,main]
2024-10-01 23:24:17.364 [main] INFO c.l.h.w.n.b.NettyApplicationWrapper.runDev:83 - new hotswap class loader:com.litongjava.hotswap.classloader.HotSwapClassLoader@c818063
此时,HotSwapWatcher
已经启动,它会持续监控 .class
文件的变化。你只需修改任意 Java 文件,并保存和编译,编译成功后 HotSwapWatcher
会通知 Netty
重启服务器,新的类将通过 HotSwapClassLoader
重新加载。
4. 使用效果
一旦实现了热加载,你可以快速迭代开发流程:
- 修改代码:在 IDE 中修改代码并保存。
- 自动编译:保存后自动编译生成新的
.class
文件。 - 热加载:
hotswap-classloader
自动重新加载新类并应用到当前运行的服务器中。 - 服务器重启:Netty 服务器自动重启,并使用新加载的类,无需手动干预。
这样,开发者无需停止和重新启动服务器,可以极大提升开发效率。
5. 完整日志
2024-10-01 23:24:17.354 [main] INFO c.l.h.w.n.b.NettyApplicationWrapper.runDev:76 - start hotswap watcher:Thread[HotSwapWatcher,10,main]
2024-10-01 23:24:17.364 [main] INFO c.l.h.w.n.b.NettyApplicationWrapper.runDev:83 - new hotswap class loader:com.litongjava.hotswap.classloader.HotSwapClassLoader@c818063
2024-10-01 23:24:17.402 [main] INFO c.l.t.u.e.EnvUtils.load:171 - app.env:null
2024-10-01 23:24:17.404 [main] INFO c.l.j.a.s.ComponentScanner.findClasses:72 - resource:file:/E:/code/java/project-litongjava/netty-boot-web-hello/target/classes/com/litongjava/study/netty/boot
2024-10-01 23:24:17.412 [main] INFO c.l.n.b.c.NettyApplicationContext.run:65 - scanned classes size:6
2024-10-01 23:24:17.456 [main] INFO c.z.h.HikariDataSource.<init>:80 - HikariPool-1 - Starting...
2024-10-01 23:24:18.012 [main] INFO c.z.h.HikariDataSource.<init>:82 - HikariPool-1 - Start completed.
2024-10-01 23:24:18.016 [main] INFO c.l.s.n.b.c.ImDbConfig.config:44 - show sql:true
2024-10-01 23:24:18.032 [main] INFO c.l.s.n.b.c.WebConfig.config:23 - :com.litongjava.netty.boot.server.NettyBootServer@42e99e4a
2024-10-01 23:24:18.033 [main] INFO c.l.s.n.b.c.WebConfig.config:39 - :com.litongjava.netty.boot.http.DefaultHttpReqeustRouter@37afeb11,com.litongjava.netty.boot.websocket.DefaultWebsocketRouter@515aebb0
2024-10-01 23:24:18.033 [main] INFO c.l.n.b.c.NettyApplicationContext.run:121 - :com.litongjava.netty.boot.server.NettyBootServer@42e99e4a,com.litongjava.netty.boot.http.DefaultHttpReqeustRouter@37afeb11,com.litongjava.netty.boot.websocket.DefaultWebsocketRouter@515aebb0
2024-10-01 23:24:18.033 [main] INFO c.l.n.b.c.NettyApplicationContext.run:123 - http mapping
{
"/echo": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$14/692331943@222545dc",
"/txt": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$12/1287934450@5c5eefef",
"/test": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$15/166794956@16293aa2",
"/json": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$13/6519275@5158b42f"
}
2024-10-01 23:24:18.033 [main] INFO c.l.n.b.c.NettyApplicationContext.run:124 - websocket mapping
{
"/ws": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$16/1388278453@595b007d"
}
2024-10-01 23:24:18.033 [main] INFO c.l.n.b.c.NettyApplicationContext.run:128 - init:34(ms),scan class:11(ms),config:618(ms),http route:0(ms)
2024-10-01 23:24:18.033 [main] INFO c.l.n.b.c.NettyApplicationContext.printUrl:151 - port:80
http://localhost/im
2024-10-01 23:24:18.538 [main] WARN i.n.b.ServerBootstrap.setChannelOption:464 - Unknown channel option 'TCP_NODELAY' for channel '[id: 0x1f921fa0]'
1353(ms)
2024-10-01 23:29:24.234 [pool-1-thread-1] INFO c.l.h.w.HotSwapWatcher.process:156 - watch event ENTRY_MODIFY,TestHandler.class
2024-10-01 23:29:24.235 [pool-1-thread-1] INFO c.l.h.w.HotSwapWatcher.process:165 - restart server:com.litongjava.hotswap.wrapper.netty.boot.NettyBootRestartServer@465d89fc
loading
2024-10-01 23:29:24.235 [pool-1-thread-1] INFO c.l.n.b.s.DefaultNettyServerBootstrap.close:55 - Closing Netty server...
2024-10-01 23:29:24.241 [pool-1-thread-1] INFO c.l.n.b.s.DefaultNettyServerBootstrap.close:70 - Netty server closed.
2024-10-01 23:29:24.242 [pool-1-thread-1] INFO c.l.h.w.n.b.NettyBootRestartServer.restart:36 - new classLoader:com.litongjava.hotswap.classloader.HotSwapClassLoader@1c957b32
2024-10-01 23:29:24.249 [pool-1-thread-1] INFO c.l.t.u.e.EnvUtils.load:171 - app.env:null
2024-10-01 23:29:24.249 [pool-1-thread-1] INFO c.l.j.a.s.ComponentScanner.findClasses:72 - resource:file:/E:/code/java/project-litongjava/netty-boot-web-hello/target/classes/com/litongjava/study/netty/boot
2024-10-01 23:29:24.251 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.run:65 - scanned classes size:6
2024-10-01 23:29:24.252 [pool-1-thread-1] INFO c.z.h.HikariDataSource.<init>:80 - HikariPool-2 - Starting...
2024-10-01 23:29:24.271 [pool-1-thread-1] INFO c.z.h.HikariDataSource.<init>:82 - HikariPool-2 - Start completed.
2024-10-01 23:29:24.272 [pool-1-thread-1] INFO c.l.s.n.b.c.ImDbConfig.config:44 - show sql:true
2024-10-01 23:29:24.273 [pool-1-thread-1] INFO c.l.s.n.b.c.WebConfig.config:23 - :com.litongjava.netty.boot.server.NettyBootServer@763ff22e
2024-10-01 23:29:24.275 [pool-1-thread-1] INFO c.l.s.n.b.c.WebConfig.config:39 - :com.litongjava.netty.boot.http.DefaultHttpReqeustRouter@54fad307,com.litongjava.netty.boot.websocket.DefaultWebsocketRouter@149b04a0
2024-10-01 23:29:24.275 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.run:121 - :com.litongjava.netty.boot.server.NettyBootServer@763ff22e,com.litongjava.netty.boot.http.DefaultHttpReqeustRouter@54fad307,com.litongjava.netty.boot.websocket.DefaultWebsocketRouter@149b04a0
2024-10-01 23:29:24.275 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.run:123 - http mapping
{
"/echo": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$22/1614062723@577a413a",
"/txt": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$20/1517207008@7cc86adf",
"/test": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$23/2051493076@7ea8ecaa",
"/json": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$21/1762886925@325a59db"
}
2024-10-01 23:29:24.275 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.run:124 - websocket mapping
{
"/ws": "com.litongjava.study.netty.boot.config.WebConfig$$Lambda$24/1255942322@37acbd3b"
}
2024-10-01 23:29:24.277 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.run:128 - init:7(ms),scan class:3(ms),config:23(ms),http route:2(ms)
2024-10-01 23:29:24.277 [pool-1-thread-1] INFO c.l.n.b.c.NettyApplicationContext.printUrl:151 - port:80
http://localhost/im
2024-10-01 23:29:24.316 [pool-1-thread-1] WARN i.n.b.ServerBootstrap.setChannelOption:464 - Unknown channel option 'TCP_NODELAY' for channel '[id: 0x4a3e2313]'
Loading complete in 82 ms (^_^)
总结
通过整合 hotswap-classloader
,Netty-Boot
项目能够实现快速的代码热加载功能。该方案非常适合频繁修改代码、调试和开发的场景,有效缩短了开发周期,同时降低了项目重启带来的时间损耗。