2025年在线Java 动态运行Java源代码-编译器

在线Java 动态运行Java源代码-编译器1 获取 JDK 编译器主要有两种方式 当然大神也可以有三种 自己写编译器 如果运行的环境 System getProperty java home 中配置了 JDK 则直接通过 ToolProvider getSystemJav 获取系统环境中 tools jar 里的编译器

大家好,我是讯享网,很高兴认识大家。

1、获取JDK编译器主要有两种方式(当然大神也可以有三种,自己写编译器):

  • 如果运行的环境System.getProperty("java.home")中配置了JDK,则直接通过ToolProvider.getSystemJavaCompiler()获取系统环境中tools.jar里的编译器;
  • 如果运行的环境没有配置JDK,就需要加载自己指定tools.jar,然后创建编译器。
public class StandardCompilerFactory implements AbstractCompilerFactory { private Logger log = Logger.getLogger(StandardCompilerFactory.class.getName()); private URL toolsUrl; public StandardCompilerFactory() { this(null); } public StandardCompilerFactory(URL toolsUrl) { super(); this.toolsUrl = toolsUrl; } / * 当toolsUrl不为null时,获取自定义工具包中的编译器;<br/> * 否则先尝试加载系统运行环境中JDK的编译器,如果未加载到编译器,则再尝试框架自带tools包(LITE不含)加载编译器。 * * @return . */ @Override public JavaCompiler getCompiler() { if (toolsUrl != null) { try { log.info("加载tools.jar路径:" + toolsUrl); // 加载自定义tools中的编译器 return loadJavaCompiler(toolsUrl); } catch (Exception e) { log.log(Level.WARNING, e.getMessage(), e); throw new AssertionError("无法获取编译器,自定义路径:" + toolsUrl); } } // 获取java的编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); if (compiler != null) { return compiler; } // 利用框架自带tools包,兜底创建编译器(LITE版无) // 创建临时暂存tools.jar String tempPath = System.getProperty("java.io.tmpdir"); File tmpJar = new File(tempPath, "dynamic_tools_1.8.jat"); URL loadUrl = null; try { loadUrl = tmpJar.toURI().toURL(); log.info("加载tools.jar路径:" + loadUrl); InputStream in = StringJavaCompiler.class.getClassLoader() .getResourceAsStream("lib/tools-1.8.jar"); if (!tmpJar.exists()) { // 拷贝到临时目录 FileOutputStream out = new FileOutputStream(tmpJar); IOUtils.copyByNIO(in, out, 2 << 10); } // 加载临时工具包 return loadJavaCompiler(loadUrl); } catch (Exception e) { log.log(Level.WARNING, e.getMessage(), e); if (tmpJar.exists()) { tmpJar.delete(); } throw new AssertionError( "无法获取编译器,默认路径:" + loadUrl + "。如果为LITE版,请务必保证运行环境有JDK或者配置自定义tools.jar路径。"); } } private JavaCompiler loadJavaCompiler(URL toolUrl) throws Exception { // 加载工具包 URL[] urls = { toolUrl }; URLClassLoader loader = new URLClassLoader(urls); // 因运行环境没有JavacTool类,所以通过反射来创建获取编译工具类 Class<?> javacTool = Class.forName("com.sun.tools.javac.api.JavacTool", true, loader); // 获取创建编译器方法 Method create = javacTool.getMethod("create"); // 获取编译器 return (JavaCompiler) create.invoke(null); } }

讯享网

 2、有编译器,那我们就可以用编译取编译Java源码,获取我们想要编译后的字节码。编译源码用到的核心类JavaFileManager、JavaFileObject。JavaFileObject主要作用是将纯源码文本来替换Java.java文件作为输入源编译,JavaFileManager这里作用是获取编译后的字节码为后面通过字节码加载Class做准备。

输入源:

讯享网/ * 自定义一个字符串的源码对象 */ public class StringJavaFileObject extends SimpleJavaFileObject { // 等待编译的源码字段 private String contents; // java源代码 => StringJavaFileObject对象 的时候使用 public StringJavaFileObject(String className, String contents) { super(URI.create( "string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE); this.contents = contents; } // 字符串源码会调用该方法 @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return contents; } }

编译后的字节:

/ * 自定义一个编译之后的字节码对象 */ public class ByteJavaFileObject extends SimpleJavaFileObject { // 存放编译后的字节码 private ByteArrayOutputStream outPutStream; public ByteJavaFileObject(String className, Kind kind) { super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), kind); } // StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream) @Override public OutputStream openOutputStream() { outPutStream = new ByteArrayOutputStream(); return outPutStream; } // 在类加载器加载的时候需要用到 public byte[] getCompiledBytes() { return outPutStream.toByteArray(); } }

 JavaFileManager:

讯享网/ * 自定义一个JavaFileManage来控制编译之后字节码的输出位置 */ public class StringJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> { private CompileResult result; public StringJavaFileManager(JavaFileManager fileManager, CompileResult result) { super(fileManager); this.result = result; } // 获取输出的文件对象,它表示给定位置处指定类型的指定类。 @Override public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { ByteJavaFileObject javaFileObject = new ByteJavaFileObject(className, kind); result.setFileObject(javaFileObject); return javaFileObject; } }

 3、最后编译源码文本,使用DiagnosticCollector收集编译信息,获取返回最终编译结果。


讯享网

 public CompileResult compile(String fullClassName, String sourceCode, CalTimeDTO calTime) throws CompileException { long compilerTakeTime = -1; boolean compileSuccess; // 存放编译过程中输出的信息 DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>(); CompileResult result = new CompileResult(fullClassName); if (calTime.isCalCompileTime()) { long startTime = System.currentTimeMillis(); // 编译成字节,并将字节对象放入result compileSuccess = compile(sourceCode, diagnosticsCollector, result); // 设置编译耗时(单位ms) compilerTakeTime = System.currentTimeMillis() - startTime; } else { // 编译成字节,并将字节对象放入result compileSuccess = compile(sourceCode, diagnosticsCollector, result); } result.setCompileTime(compilerTakeTime); if (compileSuccess) { return result; } throw new CompileException(diagnosticsCollector); } / * 核心编译 * * @param sourceCode 源码 * @param 编译错误信息 * @param result 结果 * @return 是否编译成功 */ protected boolean compile(String sourceCode, DiagnosticCollector<JavaFileObject> diagnosticsCollector, CompileResult result) { // 标准的内容管理器,更换成自己的实现,覆盖部分方法 StandardJavaFileManager standardFileManager = getCompiler() .getStandardFileManager(diagnosticsCollector, null, null); JavaFileManager javaFileManager = new StringJavaFileManager(standardFileManager, result); // 构造源代码对象 JavaFileObject javaFileObject = new StringJavaFileObject(result.getFullClassName(), sourceCode); // 获取一个编译任务 JavaCompiler.CompilationTask task = getCompiler().getTask(null, javaFileManager, diagnosticsCollector, null, null, Arrays.asList(javaFileObject)); // TODO 后期扩展支持Processors,实现类似Lombok功能 // task.setProcessors(processors); return task.call(); }

好啦,编译阶段完成(计划后续自己开发个简单语法的编译器,支持代码块、方法,不再需要完整的java代码文件结构)。

在线Java 动态运行Java源代码

小讯
上一篇 2025-03-24 15:23
下一篇 2025-02-17 12:11

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/19072.html