您的位置:首页 >如何在CentOS上实现Java代码的热编译
发布于2026-05-06 阅读(0)
扫一扫,手机访问

ja vax.tools.Ja vaCompiler 在运行时编译源码;ClassLoader 加载新生成的类;~/hotcompile
├── src
│ └── com
│ └── example
│ └── Hello.ja va
└── classes
package com.example;
public class Hello {
public String say() { return "Hello, CentOS hot compile at " + System.currentTimeMillis(); }
}
#!/usr/bin/env bash
set -e
JA VA_HOME=/usr/lib/jvm/ja va-11-openjdk # 按实际路径调整
SRC_DIR=src
OUT_DIR=classes
mkdir -p "$OUT_DIR"
"$JA VA_HOME/bin/ja vac" -d "$OUT_DIR" -cp "$OUT_DIR" "$SRC_DIR/com/example/Hello.ja va"
#!/usr/bin/env bash
# 需引入:tools.jar(JDK 8)或 jdk.compiler 模块(JDK 9+)
# 例如:JDK 8 启动参数:-cp "$OUT_DIR:$JA VA_HOME/lib/tools.jar"
# JDK 11+ 启动参数(若使用模块化,需 --add-modules jdk.compiler)
JA VA_HOME=/usr/lib/jvm/ja va-11-openjdk
OUT_DIR=classes
MAIN_CLASS=com.example.HelloRunner # 见下方 Ja va 代码
"$JA VA_HOME/bin/ja va" -cp "$OUT_DIR" "$MAIN_CLASS"
package com.example;
import ja vax.tools.*;
import ja va.io.*;
import ja va.lang.reflect.Method;
import ja va.net.URI;
import ja va.nio.file.*;
import ja va.util.Collections;
public class HelloRunner {
private static final Path SRC_DIR = Paths.get("src");
private static final Path OUT_DIR = Paths.get("classes");
private static final String CLASS_NAME = "com.example.Hello";
private static volatile Class> cachedClass = null;
private static volatile Object instance = null;
public static void main(String[] args) throws Exception {
Ja vaCompiler compiler = ToolProvider.getSystemJa vaCompiler();
if (compiler == null) throw new IllegalStateException("需使用 JDK 运行(JRE 无编译器)");
StandardJa vaFileManager fm = compiler.getStandardFileManager(null, null, null);
try {
while (true) {
// 1) 监听 .ja va 变更(简化:每次循环都尝试编译)
Path src = SRC_DIR.resolve("com/example/Hello.ja va");
if (!Files.exists(src)) { Thread.sleep(1000); continue; }
// 2) 编译
Ja vaFileObject srcFile = fm.getJa vaFileObjects(src.toFile()).iterator().next();
Ja vaCompiler.CompilationTask task = compiler.getTask(null, fm, null,
new String[]{"-d", OUT_DIR.toString()}, // 输出目录
null,
Collections.singletonList(srcFile));
boolean ok = task.call();
if (!ok) { Thread.sleep(1000); continue; }
// 3) 仅当 .class 更新时才重新加载(避免无谓 redefine)
Path cls = OUT_DIR.resolve("com/example/Hello.class");
long lastModified = Files.getLastModifiedTime(cls).toMillis();
if (cachedClass != null) {
long prev = (Long) cachedClass.getDeclaredField("LOADED_AT").get(null);
if (lastModified <= prev) { Thread.sleep(500); continue; }
}
// 4) 自定义 ClassLoader 隔离并加载新类
URLClassLoader cl = new URLClassLoader(new URL[]{OUT_DIR.toUri().toURL()},
HelloRunner.class.getClassLoader()) {
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.equals(CLASS_NAME)) {
// 打破双亲委派,优先加载新版本
Class> c = findLoadedClass(name);
if (c == null) c = findClass(name);
if (resolve) resolveClass(c);
return c;
}
return super.loadClass(name, resolve);
}
};
Class> newCls = cl.loadClass(CLASS_NAME);
// 触发类初始化,记录加载时间(字段仅用于演示)
newCls.getDeclaredField("LOADED_AT").set(null, System.currentTimeMillis());
// 5) 替换旧实例并调用
Object newInst = newCls.getDeclaredConstructor().newInstance();
Method m = newCls.getMethod("say");
System.out.println(">>> " + m.invoke(newInst));
// 6) 释放旧引用,避免 Metaspace 泄漏
instance = newInst;
cachedClass = newCls;
cl.close();
Thread.sleep(1000);
}
} finally {
fm.close();
}
}
}
tools.jar 加入 classpath;对于 JDK 9 及以上版本,则需要确保 jdk.compiler 模块在模块路径中。ClassCastException 的关键;必要时,可以针对目标类打破双亲委派模型。FileAlterationMonitor 来监听 .ja va 源文件目录和 .class 输出目录。Ja vaCompiler 进行编译;当 .class 文件更新时,则触发自定义的 ClassLoader 重新加载目标类。ClassCastException 和 Metaspace 内存泄漏的黄金法则。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8