页面上动态编译及执行java代码

本文地址:http://www.cnblogs.com/liaoyu/p/real-time-compile-and-run-java-code-web-app.html

最近看到同事在页面上编译和执行java代码,有点像Web IDE,感觉很酷就试着自己实现下。

预期要实现以下几个功能:

  1. 页面上使用textarea作为简单的代码编辑器,通过单击执行按钮向服务器发送请求
  2. 服务器端接收到java代码,进行简单的校验,若检验通过则进行编译
  3. 如果编译错误,返回异常信息
  4. 重定向代码中的system.out输出,将结果返回到客户端

相关实现

public class RuntimeCompiler {

    private List<String> options = null;
    private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    private DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
    private StringBuffer traceMsg = new StringBuffer();

    public RuntimeCompiler(String... options) {
        // inital compile params
        if (options != null && options.length > 0) {
            this.options = Arrays.asList(options);
        }
    }

    public boolean compile(String className, String code) {
        JavaFileObject sourceFile = new StringJavaFileObject(className, code);
        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(sourceFile);
        CompilationTask task = compiler.getTask(null, null, diagnostics, options, null, compilationUnits);
        boolean result = task.call();

        // Record compile error messages
        for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
            traceMsg.append(diagnostic.getMessage(null)).append("\n");
            traceMsg.append(String.format("Error on line %d in %s%n", diagnostic.getLineNumber(),
                    ((FileObject) diagnostic.getSource()).toUri()));
        }

        return result;
    }

    public String getTraceMsg() {
        return traceMsg.toString();
    }
}

编译时需指定生成的.class文件的路径,由于是在tomcat作为服务器,存在到WEB-INF\classes下即可:

String realPath = request.getServletContext().getRealPath("/") + "WEB-INF\\classes";
RuntimeCompiler rc = new RuntimeCompiler("-d", realPath);
boolean success = rc.compile(className, code);

在使用反射执行该类时,需将System.out的输出重定向到ByteArrayOutputStream,最后将该结果返回到客户端

// Create a stream to hold the output
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(baos);
// Tell Java to use your special stream
System.setOut(printStream);

if (success) {
    try {
        Class.forName(fullClassName).getDeclaredMethod("main", new Class[] {
            String[].class
        }).invoke(null, new Object[] {
            null
        });
    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
            | InvocationTargetException e) {
        result = "Load class error: " + e;
    }
}

// flush output stream 
System.out.flush();
out.print(baos.toString());

我在页面中是使用angularjs来实现交互,发现angularjs无法通过在 textarea 标签里边来设置默认值,只好用指令 ng-model 绑定变量,在controller中设置初始值。

目前存在的问题

当在页面上的修改同一个类时,连续点击run,会有10秒左右的延迟,这是由于它会编译成内容不同的.class文件,只有当 org.apache.catalina.core.StandardContext reload 才有效。

运行效果

查看完整源代码:点击这里

你可能感兴趣的:(页面上动态编译及执行java代码)