是否可以将 Java 编译器嵌入到应用程序中?

Is it possible to embed Java compiler into an application?

使用 Xtext 和 Xtend,我编写了 DSL 语法及其关联的代码生成器,它创建了一堆 Java 源文件,构成了一个 Java 应用程序。我想为我的同事提供一个程序,让他们 select 一个用我们的领域特定语言编写的文件,运行代码生成器来生成实际 Java 应用程序的 Java 源文件,编译这些文件并启动应用程序。这个过程在我的开发机器上运行良好,因为我已经安装了所有东西,包括 JDK。但是向我的同事提供此应用程序会迫使他们在使用该应用程序之前安装 JDK。所以我的问题是:是否可以将 Java 编译器嵌入到分发包中?如果没有,您是否看到其他方法可以继续?我正在使用 Gradle 和 javapackager + Inno Setup 来生成嵌入 JRE 的分发包。

更新 #1

 override public void afterGenerate(Resource input, IFileSystemAccess2 fsa, IGeneratorContext context)
 {
  // get the class loader
  val URL[] classLoaderUrls = #{new URL("file:jfxrt.jar")}
  val URLClassLoader cl = new URLClassLoader(classLoaderUrls)

  // set Java compiler options
  var CompilerOptions compilerOptions = new CompilerOptions
  compilerOptions.sourceLevel = ClassFileConstants.JDK1_8
  compilerOptions.complianceLevel = ClassFileConstants.JDK1_8
  val Map<String, String> options = new HashMap
  options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE)
  options.put(CompilerOptions.OPTION_ReportUnusedParameter, CompilerOptions.IGNORE)
  compilerOptions.set(options)

  val InMemoryJavaCompiler compiler = new InMemoryJavaCompiler(cl, compilerOptions)

  // compile all generated Java source files
  val result = compiler.compile(new JavaSource("xxxx/Bit.java", bitGen.javaSource.toString),
   new JavaSource("xxxx/BitNature.java", bitNatureGen.javaSource.toString),
   new JavaSource("xxxx/ImageMaker.java", imgMakerGen.javaSource.toString),
   new JavaSource("xxxx/MyApp.java", myAppGen.javaSource.toString))

  val URLClassLoader runcl = new URLClassLoader(classLoaderUrls, result.classLoader)

  val Class<?> mainClazz = runcl.loadClass("xxxx.MyApp")

  val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))
  val String[] args = #["C:\temp\memory_a.yyy"]
  try
  {
   mainMethod.invoke(null, #[args])
  }
  catch (InvocationTargetException e)
  {
   e.getCause().printStackTrace()
  }
  catch (Exception e)
  {
   // generic exception handling
   e.printStackTrace();
  }
 }

更新 #2

单步执行我的代码,我可以看到以下内容:

1 - 该语句执行没有问题(mainClazz 似乎是一个有效的参考)

val Class<?> mainClazz = result.classLoader.loadClass("xxxx.MyApp")

2 - 当我执行

时,mainMethod 似乎也是一个有效的参考
val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))

3 - 当我执行

时事情变糟了
mainMethod.invoke(null, #[args])

问题出现在 JavaFX 应用程序 class 中,在以下语句中启动方法(ClassNotFoundException:xxxx.MyApp)

Class theClass = Class.forName(callingClassName, false,
                               Thread.currentThread().getContextClassLoader());
package org.xtext.example.mydsl2.tests

import java.lang.reflect.Method
import java.net.URLClassLoader
import org.eclipse.xtext.util.JavaVersion
import org.eclipse.xtext.xbase.testing.InMemoryJavaCompiler
import org.eclipse.xtext.xbase.testing.JavaSource

class SampleXXX {

    def static void main(String[] args) {
        val urls = #[]
        val classLoader = new URLClassLoader(urls)
        val compiler = new InMemoryJavaCompiler(classLoader, JavaVersion.JAVA8)
        val result = compiler.compile(new JavaSource("demo/Demo.java", '''
        package demo;
        public class Demo {
            public static void main(String[] args) throws Exception {
                Class theClass = Class.forName("demo.Demo", false,
                    Thread.currentThread().getContextClassLoader());
            }
        }
        '''))
        val URLClassLoader runcl = new URLClassLoader(#[], result.classLoader)
        (new Thread() {

            override run() {
            val Class<?> mainClazz = runcl.loadClass("demo.Demo")
            val String[] args2 = #["C:\temp\memory_a.yyy"]
            val Method mainMethod = mainClazz.getMethod("main", typeof(String[]))
            mainMethod.invoke(null, #[args2])
            }

        }=>[
            contextClassLoader = runcl
        ]).start
    }

}