From 000691db86006b5be817dfa5516b303af666ea30 Mon Sep 17 00:00:00 2001 From: jaysunxiao Date: Thu, 7 May 2026 17:01:31 +0800 Subject: [PATCH] docs: Translate the Chinese comments in the event module into English. --- .../com/zfoo/hotswap/agent/HotSwapAgent.java | 24 +++--- .../hotswap/service/HotSwapServiceMBean.java | 78 +++++++++---------- .../hotswap/service/IHotSwapServiceMBean.java | 8 +- .../com/zfoo/hotswap/util/HotSwapUtils.java | 12 +-- .../com/zfoo/hotswap/ApplicationTest.java | 12 +-- .../java/com/zfoo/hotswap/HotswapClass.java | 2 +- 6 files changed, 68 insertions(+), 68 deletions(-) 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 entry : HotSwapManager.getInstance().getClassFileDefMap().entrySet()) { - logger.info("[{}].更新的类名称:[{}],更改的时间:[{}]", ++count, entry.getKey(), entry.getValue().getLastModifyTime()); + logger.info("[{}]. Updated class: [{}], modified time: [{}]", ++count, entry.getKey(), entry.getValue().getLastModifyTime()); } } @@ -106,7 +106,7 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean { ByteArrayOutputStream baos = null; DataOutputStream dos = null; try { - // 本次需要更新的Class + // Classes to update in this pass Map updateClassMap = new HashMap<>(); List fileList = FileUtils.getAllReadableFiles(new File(absolutePath)); for (File file : fileList) { @@ -122,60 +122,60 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean { } if (updateClassMap.isEmpty()) { - logger.debug("本次更新没有如何文件"); + logger.debug("No files to update in this pass"); return; } - // 验证本次更新的所有class文件是否合法 + // Validate all class files to be updated in this pass Map classFileDefMap = HotSwapContext.getHotSwapManager().getClassFileDefMap(); - for (Map.Entry entry : updateClassMap.entrySet()) {// 读取所有本次需要热更新的字节码 + for (Map.Entry entry : updateClassMap.entrySet()) {// Read all bytecode to be hot-swapped in this pass ClassFileDef classFileDef = entry.getValue(); - // 上一次热更新的文件 + // Files from the previous hot-swap ClassFileDef lastClassFileDef = classFileDefMap.get(classFileDef.getClassName()); if (lastClassFileDef != null && !classFileDef.getClassName().equals(lastClassFileDef.getClassName())) { - logger.error("本次热更新失败,两次热更新文件的类名称不一致,转换失败:[{}]-->[{}]" + logger.error("Hot-swap failed: class names inconsistent between passes [{}]-->[{}]" , classFileDef.getClassName(), lastClassFileDef.getClassName()); return; } } - // 创建临时更新流 + // Create temporary update stream baos = new ByteArrayOutputStream(1024); dos = new DataOutputStream(baos); - // 先写需要热更新类的数量 + // Write the number of classes to hot-swap first dos.writeInt(updateClassMap.size()); for (ClassFileDef classFileDef : updateClassMap.values()) { - dos.writeUTF(classFileDef.getClassName());// 写类名称 - dos.writeInt(classFileDef.getData().length);// 字节码的长度 - dos.write(classFileDef.getData());// 字节码 + dos.writeUTF(classFileDef.getClassName()); // Write class name + dos.writeInt(classFileDef.getData().length); // Write bytecode length + dos.write(classFileDef.getData()); // Write bytecode } dos.flush(); - //设置状态,准备更新 + // Set state to ready for update HotSwapManager.updateBytes = baos.toByteArray(); HotSwapManager.exception = null; - //更新 + // Apply update loadHotSwapAgent(); if (HotSwapManager.exception != null) { for (ClassFileDef classFileDef : updateClassMap.values()) { - logger.error("类[{}]热更新失败", classFileDef.getClassName()); + logger.error("Hot-swap failed for class [{}]", classFileDef.getClassName()); } - logger.error("热更新失败原因:", HotSwapManager.exception); + logger.error("Hot-swap failed: ", HotSwapManager.exception); return; } for (ClassFileDef classFileDef : updateClassMap.values()) { - logger.info("类[{}]热更新成功", classFileDef.getClassName()); + logger.info("Hot-swap successful for class [{}]", classFileDef.getClassName()); } long end = TimeUtils.currentTimeMillis(); - logger.info("本次热更新总耗时:[{}]毫秒", end - start); + logger.info("Hot-swap total time: [{}]ms", end - start); - // 更新成功,保存更新记录 + // Update successful; save update record classFileDefMap.putAll(updateClassMap); - // 删除所有被更新过的java和class文件 + // Delete all updated .java and .class files for (ClassFileDef def : updateClassMap.values()) { FileUtils.deleteFile(new File(def.getPath())); } @@ -187,13 +187,13 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean { private void loadHotSwapAgent() throws Exception { VirtualMachine vm = null; try { - // 获取运行当前这个类的jvm的pid + // Get the PID of the JVM running this class String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; - // attach到这个pid建立通信管道,让jvm加载agent + // Attach to the PID to establish a communication pipe and load the agent vm = VirtualMachine.attach(pid); vm.loadAgent(HOT_SWAP_AGENT); } catch (AgentInitializationException | AgentLoadException | AttachNotSupportedException | IOException e) { - logger.error("热更新开启VirtualMachine失败", e); + logger.error("Hot-swap: failed to attach VirtualMachine", e); throw e; } finally { try { @@ -201,7 +201,7 @@ public class HotSwapServiceMBean implements IHotSwapServiceMBean { vm.detach(); } } catch (IOException e) { - logger.error("热更新关闭vm失败", e); + logger.error("Hot-swap: failed to detach VM", e); } } } diff --git a/hotswap/src/main/java/com/zfoo/hotswap/service/IHotSwapServiceMBean.java b/hotswap/src/main/java/com/zfoo/hotswap/service/IHotSwapServiceMBean.java index 03996681..8b80b286 100644 --- a/hotswap/src/main/java/com/zfoo/hotswap/service/IHotSwapServiceMBean.java +++ b/hotswap/src/main/java/com/zfoo/hotswap/service/IHotSwapServiceMBean.java @@ -9,16 +9,16 @@ import javax.management.MXBean; public interface IHotSwapServiceMBean { /** - * 热更新相对于项目路径的文件 + * Hot-swap a file specified as a path relative to the project root * - * @param relativePath 相对路径 + * @param relativePath relative path */ void hotSwapByRelativePath(String relativePath); /** - * 热更新绝对路径的文件 + * Hot-swap a file specified by absolute path * - * @param absolutePath 热更新文件的绝对路径 + * @param absolutePath absolute path of the file to hot-swap */ void hotSwapByAbsolutePath(String absolutePath); diff --git a/hotswap/src/main/java/com/zfoo/hotswap/util/HotSwapUtils.java b/hotswap/src/main/java/com/zfoo/hotswap/util/HotSwapUtils.java index 93cf84e8..6c348351 100644 --- a/hotswap/src/main/java/com/zfoo/hotswap/util/HotSwapUtils.java +++ b/hotswap/src/main/java/com/zfoo/hotswap/util/HotSwapUtils.java @@ -20,7 +20,7 @@ import java.lang.instrument.ClassDefinition; * Hotswap java class *

* 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"); } }