diff --git a/hotswap/src/main/java/com/zfoo/hotswap/agent/HotSwapAgent.java b/hotswap/src/main/java/com/zfoo/hotswap/agent/HotSwapAgent.java index 91ee5e10..90e3ed3b 100644 --- a/hotswap/src/main/java/com/zfoo/hotswap/agent/HotSwapAgent.java +++ b/hotswap/src/main/java/com/zfoo/hotswap/agent/HotSwapAgent.java @@ -8,9 +8,9 @@ import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; /** - * 使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序, - * 甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和 Java 类操作了, - * 这样的特性实际上提供了一种虚拟机级别支持的 AOP 实现方式,使得开发者无需对 JDK 做任何升级和改动,就可以实现某些 AOP 的功能了。 + * Using Instrumentation, developers can build an agent program independent of the application to monitor and assist programs running on the JVM, + * and even replace or modify class definitions. This enables flexible runtime JVM monitoring and Java class manipulation, + * effectively providing a JVM-level AOP mechanism without any JDK changes or upgrades. * * @author godotg */ @@ -24,11 +24,11 @@ public class HotSwapAgent { /* - Java SE6开始,提供了在应用程序的VM启动后在动态添加代理的方式,即agentmain方式。 与Permain类似,agent方式同样需要提供一个agent jar,并且这个jar需要满足: - 在manifest中指定Agent-Class属性,值为代理类全路径 - 代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。 - 不过如此设计的再运行时进行代理有个问题——如何在应用程序启动之后再开启代理程序呢? JDK6中提供了Java Tools API,其中Attach API可以满足这个需求。 - Attach API中的VirtualMachine代表一个运行中的VM。其提供了loadAgent()方法,可以在运行时动态加载一个代理jar。 + Since Java SE 6, agents can be attached dynamically after the JVM starts via the agentmain method. Like premain, it requires an agent JAR that satisfies: + - Manifest must specify the Agent-Class attribute with the fully-qualified agent class name + - The agent class must provide agentmain(String args, Instrumentation inst) or agentmain(String args); the former takes priority if both exist. args and inst are the same as in premain. + However, this raises a question: how to attach an agent after the application has started? JDK6's Java Tools API provides the Attach API to solve this. + VirtualMachine in the Attach API represents a running JVM and provides loadAgent() to dynamically load an agent JAR at runtime. */ public static void agentmain(String args, Instrumentation inst) { Class> clazz = null; @@ -40,16 +40,16 @@ public class HotSwapAgent { int len = dis.readInt(); ClassDefinition[] classDefs = new ClassDefinition[len]; for (int i = 0; i < len; i++) { - //类名 + // Class name String className = dis.readUTF(); - //类字节码 + // Class bytecode byte[] classBytes = new byte[dis.readInt()]; dis.readFully(classBytes); classDefs[i] = new ClassDefinition(Class.forName(className), classBytes); } - //开始更新 + // Start update inst.redefineClasses(classDefs); - //更新成功 + // Update successful } catch (Exception e) { if (clazz != null) { try { diff --git a/hotswap/src/main/java/com/zfoo/hotswap/service/HotSwapServiceMBean.java b/hotswap/src/main/java/com/zfoo/hotswap/service/HotSwapServiceMBean.java index 2491b36d..1c2b0977 100644 --- a/hotswap/src/main/java/com/zfoo/hotswap/service/HotSwapServiceMBean.java +++ b/hotswap/src/main/java/com/zfoo/hotswap/service/HotSwapServiceMBean.java @@ -24,17 +24,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -//java11过后ClassFile对外不可见 +// After Java 11, ClassFile is no longer publicly visible //import com.sun.tools.classfile.ClassFile; /** - * JMX(JAVA Management Extensions)技术是java5的新特性,它提供一种简单,标准的方式去管理应用程序,设备,服务等资源。 - * JMS定义了一些设计模式,api和一些服务来进行应用程序和网络的监控,这些都是基于java语言环境的。 - * 使用JMS技术,资源被一种叫做MBeans(Managed Beans)监控,这些MBean都在一个核心对象管理server上注册。 - * JMS给java开发者提供了自由的方式去监控java代码,创建智能java agents,实现分布式管理的中间件和管理者,并且能够快速整合这些方案到的管理和监控系统。 + * JMX (Java Management Extensions) is a Java 5 feature providing a simple, standard way to manage applications, devices, and services. + * JMX defines design patterns, APIs, and services for monitoring applications and networks within the Java environment. + * With JMX, resources are managed by MBeans (Managed Beans) registered with a central MBean server. + * JMX gives Java developers a flexible way to monitor Java code, create intelligent agents, build distributed management middleware, and integrate into management/monitoring systems. *
- * 根据JMX描述,MBean接口包括一些可读或者可写的属性,还有一些定义好的方法,这些方法能够被MBean管理应用程序调用。
- * 实现类,类名必须为接口sufixMBean的前缀。也就是Hello。如果不按这个命名注册MBean就会有问题。
+ * Per the JMX spec, an MBean interface exposes readable/writable attributes and defined methods callable by management applications.
+ * Implementation class name must be the interface name without the 'MBean' suffix (e.g., 'Hello' for 'HelloMBean'). Non-compliance causes registration errors.
*
* @author godotg
*/
@@ -44,7 +44,7 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean {
private static final Logger logger = LoggerFactory.getLogger(HotSwapServiceMBean.class);
- // 热更新的代理和热更新的文件要放在同一个目录
+ // The hot-swap agent and hot-swap files must be in the same directory
private static final String HOT_SWAP_SCRIPT = "hotscript";
private static final String HOT_SWAP_AGENT = HOT_SWAP_SCRIPT + "/hotswap-2.0.jar";
@@ -57,15 +57,15 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean {
registerMBean();
}
- // 使用jconsole去连接,当然也可以使用RMI进行远程连接MBean server,来进行管理和执行操作。
+ // Connect via jconsole or remotely via RMI to the MBean server for management
private void registerMBean() {
- //注册监控
+ // Register monitoring
try {
- //获取MBeanServer 如果没有MBean server存在那么下面会自动调用ManagementFactory.createMBeanServer()
+ // Get MBeanServer; if none exists, ManagementFactory.createMBeanServer() is called automatically
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
- // 包名加 类名 创建一个ObjectName
+ // Create ObjectName from package + class name
ObjectName objectName = new ObjectName(this.getClass().getPackage().getName() + ":type=" + this.getClass().getSimpleName());
- // 在MBean server上注册MBean
+ // Register MBean with the MBean server
mbs.registerMBean(this, objectName);
} catch (Exception e) {
logger.error("MBean error", e);
@@ -83,11 +83,11 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean {
@Override
public synchronized void hotSwapByAbsolutePath(String absolutePath) {
- // 热更新class文件,上一步没有完成的操作本次继续执行
+ // Hot-swap class files; continue any operations not completed in the previous pass
try {
hotSwapClass(absolutePath);
} catch (Exception e) {
- logger.error("热更新class文件异常:[exception:{}]", e);
+ logger.error("Hot-swap class file error: [exception:{}]", e);
}
}
@@ -95,7 +95,7 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean {
public void logAllUpdateClassFileInfo() {
int count = 0;
for (Map.Entry
* EN: Prefer using simple Javassist for hot updates, followed by Byte Buddy hot updates
- * CN: 优先使用简单的Javassist做热更新,因为Byte Buddy使用了更为复杂的ASM,spring boot web项目中会优先使用Byte Buddy热更新
+ * Prefer Javassist for hot-swapping (simpler); Byte Buddy uses more complex ASM. Spring Boot web projects prefer Byte Buddy for hot-swapping
*
* @author godotg
*/
@@ -31,13 +31,13 @@ public abstract class HotSwapUtils {
/**
* need to add JVM startup parameters: -Djdk.attach.allowAttachSelf=true
*
- * JVM的启动参数,jdk11过后默认JVM不允许连接自己,所以需要自己添加 -Djdk.attach.allowAttachSelf=true 启动参数
+ * JVM startup parameter: since JDK 11, the JVM does not allow self-attachment by default. Add -Djdk.attach.allowAttachSelf=true to enable it
*
- * note: 需要配置 JAVA_HOME 环境变量,如果没有配置这个环境变量可能会导致未知异常
- * note: 使用 -cp 或者 -Djava.ext.dirs 参数可能会导致热更新未知异常,推荐使用maven-shade-plugin或者spring-boot-maven-plugin将工程打包进一个jar里。
- * note: 使用 -cp热更时会有概率出现java.lang.NoClassDefFoundError,其中多现于Lambda表达式和静态Enum的类中
+ * note: JAVA_HOME environment variable must be configured; unknown errors may occur without it
+ * note: Using -cp or -Djava.ext.dirs may cause unknown hot-swap errors; recommend packaging with maven-shade-plugin or spring-boot-maven-plugin
+ * note: Using -cp may occasionally cause java.lang.NoClassDefFoundError, especially in Lambda expressions and static Enum classes
*
- * @param bytes .class结尾的字节码文件
+ * @param bytes bytecode from a .class file
*/
public static synchronized void hotswapClass(byte[] bytes) {
if (ArrayUtils.isEmpty(bytes)) {
diff --git a/hotswap/src/test/java/com/zfoo/hotswap/ApplicationTest.java b/hotswap/src/test/java/com/zfoo/hotswap/ApplicationTest.java
index 73537ead..8a91b2e7 100644
--- a/hotswap/src/test/java/com/zfoo/hotswap/ApplicationTest.java
+++ b/hotswap/src/test/java/com/zfoo/hotswap/ApplicationTest.java
@@ -12,17 +12,17 @@ import java.io.IOException;
*/
public class ApplicationTest {
- // 热更新教程,需要添加JVM参数,-Djdk.attach.allowAttachSelf=true,如果不加这个参数将使用Byte Buddy热更新替代Javassist热更新
- // 使用Javassist热更新更加的轻量,如果Javassist热更新失败,则会自动使用Byte Buddy做热更新
+ // Hot-swap tutorial: add JVM arg -Djdk.attach.allowAttachSelf=true; without it, Byte Buddy hot-swap is used instead of Javassist
+ // Javassist hot-swap is lighter; if it fails, Byte Buddy is used automatically
@Test
public void startHotSwapTest() throws IOException {
- // 热更新限制,不能为需要热更新的类添加或减少成员函数和成员变量,只能修改函数内部的逻辑
+ // Hot-swap limitation: cannot add or remove methods/fields; only internal logic may be changed
var test = new HotswapClass();
- // 没有热更新的输出
+ // Output before hot-swap
test.print();
- // 随便修改print方法,然后编译成为一个需要热更新的class文件
+ // Modify the print method, then compile it to a .class file to be hot-swapped
HotSwapUtils.hotswapClass(IOUtils.toByteArray(ClassUtils.getFileFromClassPath("HotswapClass.class")));
- // 热更新之后的输出
+ // Output after hot-swap
test.print();
}
diff --git a/hotswap/src/test/java/com/zfoo/hotswap/HotswapClass.java b/hotswap/src/test/java/com/zfoo/hotswap/HotswapClass.java
index b655bda1..b07bf952 100644
--- a/hotswap/src/test/java/com/zfoo/hotswap/HotswapClass.java
+++ b/hotswap/src/test/java/com/zfoo/hotswap/HotswapClass.java
@@ -11,7 +11,7 @@ public class HotswapClass {
private static final Logger logger = LoggerFactory.getLogger(HotswapClass.class);
public void print() {
- logger.info("没有热更新的输出内容");
+ logger.info("No hot-swap output content");
}
}