实现在Web运行Java代码(模拟刷题网站的代码提交功能)

一、前言

相信大家都在各种网站做过算法题。
而在那些网站通过文本框提交代码之后的即时编译运行是怎么做到的呢。

二、思路

注:这篇文章的方法只适用于java(不过能生成class文件的语言也都能套用)

其实一开始要实现这个功能是相当迷茫的,在网上搜不知道该搜什么,也包括这篇文章起标题名都不知道该叫什么。
查了一会果断开始自己造轮子了。

方案1

在自己的web上写一套可以跑java语言的编译器和解释器。

显然方案1不太可行(时间成本略高),不过也不排除有套件可以用。

方案2

让客户自己生成java或者class文件提交上来,在服务器运行。

方案2的话,用户自己编译文件提交文件,前提还得用户有java环境。

方案3

客户提交String形式的代码,服务器将String编译成class,然后服务器运行。

只有3是最亲民的方案了。

三、实现

找到思路之后,剩下的就是凑代码了。
刚好本身又了解一些相关的知识。
简单来说只要实现两个东西:

1.从request请求中获取用户的代码串,编译成class文件。
2.使用ClassLoader加载class文件。

为什么用自定义的ClassLoader而不用class.forName()
不知道的话就百度吧,建议了解。

对代码就不做详细的解释了。简单直白。

MyCompiler.java
package compile;

import classLoader.MyClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MyCompiler {
    static String outDir = System.getProperty("user.dir") + "\\src";
    public void compile(String name, String content) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        StrSrcJavaObject srcObject = new StrSrcJavaObject(name, content);
        List<StrSrcJavaObject> fileObjects = Collections.singletonList(srcObject);
        String flag = "-d";
        System.out.println("输出目录为" + outDir);
        Iterable<String> options = Arrays.asList(flag, outDir);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects);
        boolean result = task.call();
        if (result) System.out.println("Compile it successfully.");
    }

    private static class StrSrcJavaObject extends SimpleJavaFileObject {
        private final String content;
        public StrSrcJavaObject(String name, String content) {
            super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
            this.content = content;
        }
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return content;
        }
    }
}
MyClassLoader.java
package classLoader;
import java.io.*;
public class MyClassLoader extends ClassLoader {
    private final String classpath;
    public MyClassLoader() {
        this.classpath = System.getProperty("user.dir") + "\\src";
    }
    public MyClassLoader(String classpath) {
        this.classpath = classpath;
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            byte[] classDate = getDate(name);
            //defineClass方法将字节码转化为类
            if (classDate != null) return defineClass(name, classDate, 0, classDate.length);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return super.findClass(name);
    }
    //返回类的字节码
    private byte[] getDate(String className) throws IOException {
        String path = classpath + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
        try (InputStream in = new FileInputStream(path); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[2048];
            int len;
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
TestServlet.java
package mservlet;
import classLoader.MyClassLoader;
import compile.MyCompiler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String opt = req.getParameter("opt");
        String content = req.getParameter("content");
        if(opt.equals("0")){
            //String content = "package play; public class Test{ public static void main(String[] args){System.out.println(\"compile test.\");} }";
            Class<?> cls = MyCompiler.compile("play.Test", content);
        }else{
            MyClassLoader myClassLoader = new MyClassLoader();
            try {
                Class c = myClassLoader.loadClass("play.Test");
                Method method = c.getMethod("main", String[].class);
                method.invoke(null, new Object[]{new String[]{}});
                req.setAttribute("resp","成功运行真棒");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        req.getRequestDispatcher("/index.jsp").forward(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>mservlet.TestServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/test</url-pattern>
    </servlet-mapping>
</web-app>
index.jsp
<%--
  Created by IntelliJ IDEA.
  User: LICHEN
  Date: 2020/5/10
  Time: 19:48
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>$Title$</title>
</head>
<form action="${pageContext.request.contextPath}/test" METHOD="post">
    <input type="submit" value="test">
    content<textarea rows="8" name="content"></textarea>
    opt<input type="text" name="opt" value="0">
</form>
<h2>${resp}</h2>
<body>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值