Java 动态编译 Java 代码,idea 调试 jar 文件
1. 项目结构

各模块介绍
compile-dao:dao 层,数据库持久化层,本文暂时用不到
compile-pojo:实体类
compile-service:业务逻辑模块
compile-shell:Java 动态编译的一些 java 文件
compile-web:SpringBoot 的入口
springboot-java-compile 父工程 pom.xml
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<modelVersion>4.0.0</modelVersion>
-
<packaging>pom</packaging>
-
<modules>
-
<module>compile-shell</module>
-
<module>compile-service</module>
-
<module>compile-web</module>
-
<module>compile-dao</module>
-
<module>compile-pojo</module>
-
</modules>
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>2.1.2.RELEASE</version>
-
<relativePath/> <!-- lookup parent from repository -->
-
</parent>
-
<groupId>com.wjx</groupId>
-
<artifactId>springboot-java-compile</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
<name>springboot-java-compile</name>
-
<description>Demo project for Spring Boot</description>
-
-
<properties>
-
<java.version>1.8</java.version>
-
</properties>
-
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-web</artifactId>
-
</dependency>
-
</dependencies>
-
-
<!--指定使用maven打包-->
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.apache.maven.plugins</groupId>
-
<artifactId>maven-compiler-plugin</artifactId>
-
<version>3.1</version>
-
<configuration>
-
<source>${java.version}</source>
-
<target>${java.version}</target>
-
</configuration>
-
</plugin>
-
-
<plugin>
-
<groupId>org.apache.maven.plugins</groupId>
-
<artifactId>maven-surefire-plugin</artifactId>
-
<version>2.19.1</version>
-
<configuration>
-
<skipTests>true</skipTests> <!--默认关掉单元测试 -->
-
</configuration>
-
</plugin>
-
</plugins>
-
</build>
-
-
</project>
compile-shell 模块

pom.xml
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<parent>
-
<artifactId>springboot-java-compile</artifactId>
-
<groupId>com.wjx</groupId>
-
<version>0.0.1-SNAPSHOT</version>
-
</parent>
-
<modelVersion>4.0.0</modelVersion>
-
-
<artifactId>compile-shell</artifactId>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>com.alibaba</groupId>
-
<artifactId>fastjson</artifactId>
-
<version>1.2.54</version>
-
</dependency>
-
<dependency>
-
<groupId>org.projectlombok</groupId>
-
<artifactId>lombok</artifactId>
-
</dependency>
-
</dependencies>
-
</project>
JavaShellExecutor.java
-
package com.compile.executor;
-
-
import com.compile.shell.*;
-
import lombok.Data;
-
-
import java.lang.reflect.Method;
-
-
/**
-
* @Description: 获取编译后的结果
-
* @Auther: wjx
-
* @Date: 2019/1/29 15:45
-
*/
-
-
public class JavaShellExecutor {
-
-
/**
-
* 获取类名,className等于ruleName
-
*
-
* @param ruleName
-
* @return
-
*/
-
public String getClassName(String ruleName) {
-
String className = ruleName.replaceAll("[^a-z^A-Z]", "");
-
return className.substring(0, 1).toUpperCase() + className.substring(1);
-
}
-
-
/**
-
* 获取编译的结果
-
*
-
* @param className
-
* @param classObject
-
* @param ruleName
-
* @return
-
*/
-
public Class<?> getCompileResult(String className, JavaClassObject classObject, String ruleName) {
-
//使用新的自定义Classloader,记得每个规则使用一个新的classloader,
-
//当规则更新时,老的classloader可被释放
-
try {
-
return getLoadClass(className, classObject, ruleName);
-
} catch (Throwable e) {
-
throw new RuntimeException("load class error!" + ",className=" + className + "\r\n" + e.getMessage(), e);
-
}
-
}
-
-
/**
-
* 获取加载的Class
-
*
-
* @param className
-
* @param classObject
-
* @param ruleName
-
* @return
-
*/
-
public Class<?> getLoadClass(String className, JavaClassObject classObject, String ruleName) {
-
if (DataEventFactory.eventClassLoaders.get(ruleName) == null) {
-
DynamicEngine.getInstance().createNewClassLoader(ruleName);
-
}
-
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(DataEventFactory.eventClassLoaders.get(ruleName));
-
Class aClass = dynamicClassLoader.loadClass(JavaSourceObject.packageName + ruleName, classObject);
-
return aClass;
-
}
-
-
/**
-
* 执行方法并返回结果
-
*
-
* @param aClass
-
* @param methodName
-
* @return
-
*/
-
public Object getMethodResult(Class aClass, String methodName, Object... params) {
-
Object result = null;
-
try {
-
Object instance = aClass.newInstance();
-
Method method = aClass.getMethod(methodName, String.class);
-
result = method.invoke(instance, params);
-
System.out.println(result);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return result;
-
}
-
-
}
ClassFileManager.java
-
package com.compile.shell;
-
-
import javax.tools.*;
-
import java.io.IOException;
-
-
/**
-
* @Description: 类文件管理器
-
* @Auther: wjx
-
* @Date: 2019/1/18 14:22
-
*/
-
public class ClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
-
-
-
private JavaClassObject javaClassObject;
-
-
protected ClassFileManager(StandardJavaFileManager standardJavaFileManager) {
-
super(standardJavaFileManager);
-
}
-
-
public JavaClassObject getJavaClassObject() {
-
return javaClassObject;
-
}
-
-
/**
-
* 源文件被编译成 .class 文件
-
*
-
* @param location
-
* @param className
-
* @param kind
-
* @param sibling
-
* @return
-
* @throws IOException
-
*/
-
-
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
-
javaClassObject = new JavaClassObject(className, kind);
-
return javaClassObject;
-
}
-
}
DataEventFactory.java
-
package com.compile.shell;
-
-
import java.net.URLClassLoader;
-
import java.util.Map;
-
import java.util.concurrent.ConcurrentHashMap;
-
-
/**
-
* @Description: 创建一个存放ClassLoad的类
-
* @Auther: wjx
-
* @Date: 2019/1/18 15:47
-
*/
-
public class DataEventFactory {
-
/**
-
* 存放当前的ClassLoader
-
*/
-
public static Map<String, URLClassLoader> eventClassLoaders = new ConcurrentHashMap<>();
-
}
DynamicClassLoader.java
-
package com.compile.shell;
-
-
-
import java.net.URL;
-
import java.net.URLClassLoader;
-
-
/**
-
* @Description: 读取java文件编译结果
-
* @Auther: wjx
-
* @Date: 2019/1/18 15:41
-
*/
-
public class DynamicClassLoader extends URLClassLoader {
-
-
public DynamicClassLoader(ClassLoader parent) {
-
super(new URL[0], parent);
-
}
-
-
public Class findClassByName(String className) throws ClassNotFoundException {
-
return this.findClass(className);
-
}
-
-
/**
-
* 加载读取返回的类
-
* @param fullName fullName = packageName + className
-
* @param classObject
-
* @return
-
*/
-
public Class loadClass(String fullName, JavaClassObject classObject) {
-
byte[] bytes = classObject.getBytes();
-
return this.defineClass(fullName, bytes, 0, bytes.length);
-
}
-
}
DynamicEngine.java 核心类
-
package com.compile.shell;
-
-
import com.compile.executor.JavaShellExecutor;
-
-
import javax.tools.*;
-
import java.io.File;
-
import java.net.URL;
-
import java.net.URLClassLoader;
-
import java.util.ArrayList;
-
import java.util.List;
-
-
/**
-
* Compile动态执行java的String代码到内存中并执行
-
* 1.创建 URLClassLoader 类加载器
-
* 2.获取当前执行的classpath的所有jar包的路径
-
* 3.通过java的ToolProvider创建JavaCompile,用来执行class源文件
-
* 4.创建DiagnosticCollector用来执行获取执行失败的错误结果
-
* 5.添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等
-
* 6.传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java'
-
* 7.创建任务并执行
-
* 8.获取执行完成后的返回JavaClassObject类
-
* 9.创建DynamicClassLoader来加载类 ,defineClass这个方法
-
*
-
* @Description:
-
* @Auther: wjx
-
* @Date: 2019/1/18 14:55
-
*/
-
public class DynamicEngine extends JavaShellExecutor {
-
-
private static DynamicEngine dynamicEngine;
-
-
/**
-
* 单例模式
-
*
-
* @return
-
*/
-
public static DynamicEngine getInstance() {
-
if (dynamicEngine == null) {
-
synchronized (DynamicEngine.class) {
-
if (dynamicEngine == null) {
-
dynamicEngine = new DynamicEngine();
-
}
-
}
-
}
-
return dynamicEngine;
-
}
-
-
//创建动态加载jar包
-
private URLClassLoader classLoader;
-
//当前的classpath环境
-
private String classpath;
-
-
private DynamicEngine() {
-
//获取类加载器
-
this.classLoader = (URLClassLoader) this.getClass().getClassLoader();
-
-
this.buildClasspath();
-
}
-
-
private void buildClasspath() {
-
//初始化classpath为null
-
this.classpath = null;
-
StringBuilder sb = new StringBuilder();
-
for (URL url : this.classLoader.getURLs()) {
-
String f = url.getFile();
-
sb.append(f).append(File.pathSeparator);
-
}
-
this.classpath = sb.toString();
-
}
-
-
-
/**
-
* 编译
-
*
-
* @param className
-
* @param javaCode
-
* @return
-
*/
-
public Object javaCodeToObject(String className, String javaCode) {
-
//记录编译起始时间
-
long startTime = System.currentTimeMillis();
-
-
//通过java工具获取编译的compile
-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-
//创建DiagnosticCollector对象
-
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
-
-
//建立一个用于保存java文件管理器ClassFileManage
-
//每一个文件被保存在JavaClassObject的类里面
-
ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnosticCollector, null, null));
-
-
//使用编译选项可以默认编译行为,编译选项是一个String的Iterable集合
-
List<String> options = new ArrayList<>();
-
options.add("-encoding");
-
options.add("UTF-8");
-
options.add("-classpath");
-
options.add(this.classpath);
-
-
//传递源文件,是一个JavaFileObject的集合
-
List<JavaFileObject> fileObjectList = new ArrayList<>();
-
fileObjectList.add(new JavaSourceObject(className, javaCode));
-
-
//创建任务
-
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticCollector, options, null, fileObjectList);
-
//执行编译
-
Boolean call = task.call();
-
if (call) {
-
//编译成功
-
System.out.println("编译成功");
-
} else {
-
//编译失败
-
System.out.println("编译失败");
-
String error = null;
-
for (Diagnostic diagnostic : diagnosticCollector.getDiagnostics()) {
-
error += compilePrint(diagnostic);
-
}
-
throw new RuntimeException(error);
-
}
-
return fileManager.getJavaClassObject();
-
}
-
-
/**
-
* @param diagnostic
-
* @return
-
* @MethodName : compilePrint
-
* @Description : 输出编译错误信息
-
*/
-
private String compilePrint(Diagnostic diagnostic) {
-
-
System.out.println("Code:" + diagnostic.getCode());
-
System.out.println("Kind:" + diagnostic.getKind());
-
System.out.println("Position:" + diagnostic.getPosition());
-
System.out.println("Start Position:" + diagnostic.getStartPosition());
-
System.out.println("End Position:" + diagnostic.getEndPosition());
-
System.out.println("Source:" + diagnostic.getSource());
-
System.out.println("Message:" + diagnostic.getMessage(null));
-
System.out.println("LineNumber:" + diagnostic.getLineNumber());
-
System.out.println("ColumnNumber:" + diagnostic.getColumnNumber());
-
StringBuffer res = new StringBuffer();
-
res.append("Code:[" + diagnostic.getCode() + "]\n");
-
res.append("Kind:[" + diagnostic.getKind() + "]\n");
-
res.append("Position:[" + diagnostic.getPosition() + "]\n");
-
res.append("Start Position:[" + diagnostic.getStartPosition() + "]\n");
-
res.append("End Position:[" + diagnostic.getEndPosition() + "]\n");
-
res.append("Source:[" + diagnostic.getSource() + "]\n");
-
res.append("Message:[" + diagnostic.getMessage(null) + "]\n");
-
res.append("LineNumber:[" + diagnostic.getLineNumber() + "]\n");
-
res.append("ColumnNumber:[" + diagnostic.getColumnNumber() + "]\n");
-
-
return res.toString();
-
}
-
-
/**
-
* 创建一个新的ClassLoader
-
*
-
* @param ruleName
-
*/
-
public void createNewClassLoader(String ruleName) {
-
try {
-
DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.classLoader);
-
DataEventFactory.eventClassLoaders.put(ruleName, dynamicClassLoader);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
}
JavaClassObject.java
-
package com.compile.shell;
-
-
import javax.tools.SimpleJavaFileObject;
-
import java.io.ByteArrayOutputStream;
-
import java.io.IOException;
-
import java.io.OutputStream;
-
import java.net.URI;
-
-
/**
-
* @Description: 获取编译后的class文件
-
* @Auther: wjx
-
* @Date: 2019/1/18 14:25
-
*/
-
public class JavaClassObject extends SimpleJavaFileObject {
-
-
protected final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
-
/**
-
* @param name
-
* @param kind
-
*/
-
protected JavaClassObject(String name, Kind kind) {
-
super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
-
}
-
-
-
public byte[] getBytes() {
-
return bos.toByteArray();
-
}
-
-
-
-
public OutputStream openOutputStream() throws IOException {
-
return bos;
-
}
-
}
JavaSourceObject.java
-
package com.compile.shell;
-
-
import javax.tools.SimpleJavaFileObject;
-
import java.io.IOException;
-
import java.net.URI;
-
-
/**
-
* @Description: 传递java源文件
-
* @Auther: wjx
-
* @Date: 2019/1/18 14:45
-
*/
-
public class JavaSourceObject extends SimpleJavaFileObject {
-
-
public static String packageName = "com.compile.";
-
-
private String javaCode;
-
-
/**
-
* 传入.java文件
-
*/
-
protected JavaSourceObject(String className, String javaCode) {
-
super(URI.create("string:///" + className.replace(",", "/") +
-
Kind.SOURCE.extension), Kind.SOURCE);
-
this.javaCode = javaCode;
-
}
-
-
-
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
-
return javaCode;
-
}
-
}
compile-pojo 模块

pom.xml
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<parent>
-
<artifactId>springboot-java-compile</artifactId>
-
<groupId>com.wjx</groupId>
-
<version>0.0.1-SNAPSHOT</version>
-
</parent>
-
<modelVersion>4.0.0</modelVersion>
-
-
<artifactId>compile-pojo</artifactId>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>org.projectlombok</groupId>
-
<artifactId>lombok</artifactId>
-
<optional>true</optional>
-
</dependency>
-
</dependencies>
-
</project>
CompilePojo.java
-
package com.compile.pojo;
-
-
import lombok.Data;
-
-
/**
-
* @Description: 编译实体类
-
* @Auther: wjx
-
* @Date: 2019/1/29 14:52
-
*/
-
-
public class CompilePojo {
-
/**
-
* 规则名称
-
*/
-
private String ruleName;
-
-
/**
-
* 包名称
-
*/
-
private String packageName;
-
-
/**
-
* 类名称
-
*/
-
private String className;
-
-
/**
-
* java代码片段
-
*/
-
private String javaCode;
-
-
/**
-
* 方法名
-
*/
-
private String methodName;
-
/**
-
* 参数值
-
*/
-
public String params;
-
}
compile-dao 模块
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<parent>
-
<artifactId>springboot-java-compile</artifactId>
-
<groupId>com.wjx</groupId>
-
<version>0.0.1-SNAPSHOT</version>
-
</parent>
-
<modelVersion>4.0.0</modelVersion>
-
-
<artifactId>compile-dao</artifactId>
-
-
-
<dependencies>
-
-
<dependency>
-
<groupId>com.wjx</groupId>
-
<artifactId>compile-shell</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
</dependency>
-
-
<dependency>
-
<groupId>com.wjx</groupId>
-
<artifactId>compile-pojo</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
</dependency>
-
-
</dependencies>
-
</project>
compile-service 模块

pom.xml
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<parent>
-
<artifactId>springboot-java-compile</artifactId>
-
<groupId>com.wjx</groupId>
-
<version>0.0.1-SNAPSHOT</version>
-
</parent>
-
<modelVersion>4.0.0</modelVersion>
-
-
<artifactId>compile-service</artifactId>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>com.wjx</groupId>
-
<artifactId>compile-dao</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
</dependency>
-
</dependencies>
-
</project>
CompileService.java
-
package com.compile.service;
-
-
/**
-
* @Description:
-
* @Auther: wjx
-
* @Date: 2019/1/30 10:58
-
*/
-
-
public interface CompileService {
-
-
/**
-
* 执行
-
*
-
* @param ruleName
-
* @param javaCode
-
* @return
-
*/
-
Object execute(String ruleName, String javaCode);
-
-
/**
-
* 执行方法
-
*
-
* @param ruleName
-
* @param javaCode
-
* @param methodName
-
* @param parameter
-
* @return
-
*/
-
Object executeMethod(String ruleName, String javaCode, String methodName, Object... parameter);
-
}
CompileServiceImpl.java
-
package com.compile.service.impl;
-
-
import com.compile.executor.JavaShellExecutor;
-
import com.compile.service.CompileService;
-
import com.compile.shell.DynamicEngine;
-
import com.compile.shell.JavaClassObject;
-
import org.springframework.stereotype.Service;
-
-
import java.util.Map;
-
import java.util.concurrent.ConcurrentHashMap;
-
-
/**
-
* @Description:
-
* @Auther: wjx
-
* @Date: 2019/1/30 10:59
-
*/
-
-
public class CompileServiceImpl extends JavaShellExecutor implements CompileService {
-
-
private Map<String, Class<?>> executorMap = new ConcurrentHashMap<>();
-
-
/**
-
* 执行
-
*
-
* @param ruleName
-
* @param javaCode
-
* @return
-
*/
-
-
public Object execute(String ruleName, String javaCode) {
-
Class<?> aClass = compileGetClass(ruleName, javaCode);
-
Object o = null;
-
try {
-
o = aClass.newInstance();
-
System.out.println(o);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return o;
-
}
-
-
/**
-
* 执行方法
-
*
-
* @param ruleName
-
* @param javaCode
-
* @param methodName
-
* @param parameter
-
* @return
-
*/
-
-
public Object executeMethod(String ruleName, String javaCode, String methodName, Object... parameter) {
-
if (executorMap.get(ruleName) == null) {
-
execute(ruleName, javaCode);
-
}
-
Class<?> aClass = executorMap.get(ruleName);
-
try {
-
return getMethodResult(aClass, methodName, parameter);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
return null;
-
}
-
-
/**
-
* 编译Java文件
-
*
-
* @param ruleName
-
* @param javaCode
-
* @return
-
*/
-
private Class<?> compileGetClass(String ruleName, String javaCode) {
-
//先清空当前的ruleName的值
-
executorMap.remove(ruleName);
-
JavaClassObject classObject = (JavaClassObject) DynamicEngine.getInstance().javaCodeToObject(getClassName(ruleName), javaCode);
-
//获取编译的结果
-
Class<?> compileResult = getCompileResult(getClassName(ruleName), classObject, ruleName);
-
executorMap.put(ruleName, compileResult);
-
return compileResult;
-
}
-
-
}
compile-web 模块

pom.xml
-
-
<project xmlns="http://maven.apache.org/POM/4.0.0"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
<parent>
-
<artifactId>springboot-java-compile</artifactId>
-
<groupId>com.wjx</groupId>
-
<version>0.0.1-SNAPSHOT</version>
-
</parent>
-
<modelVersion>4.0.0</modelVersion>
-
-
<artifactId>compile-web</artifactId>
-
-
-
<dependencies>
-
<dependency>
-
<groupId>com.wjx</groupId>
-
<artifactId>compile-service</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
</dependency>
-
</dependencies>
-
-
-
<!--打包-->
-
<build>
-
<plugins>
-
<plugin>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-maven-plugin</artifactId>
-
<configuration>
-
<!-- 指定该Main Class为全局的唯一入口 -->
-
<mainClass>com.Application</mainClass>
-
<!--<layout>ZIP</layout>-->
-
</configuration>
-
<executions>
-
<execution>
-
<phase>package</phase>
-
<goals>
-
<goal>repackage</goal>
-
</goals>
-
</execution>
-
</executions>
-
</plugin>
-
</plugins>
-
</build>
-
</project>
CompileController.java
-
package com.compile.controller;
-
-
import com.alibaba.fastjson.JSONObject;
-
import com.compile.pojo.CompilePojo;
-
import com.compile.service.CompileService;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.web.bind.annotation.RequestBody;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.RestController;
-
-
/**
-
* @Description:
-
* @Auther: wjx
-
* @Date: 2019/1/30 11:13
-
*/
-
-
public class CompileController {
-
-
-
private CompileService compileService;
-
-
/**
-
* 编译
-
*
-
* @param compilePojo
-
* @return
-
*/
-
-
public Object execute( CompilePojo compilePojo) {
-
JSONObject result = new JSONObject();
-
try {
-
Object returnData = compileService.execute(compilePojo.getRuleName(), compilePojo.getJavaCode());
-
result.put("code", "编译成功,内容是:" + returnData);
-
} catch (Exception e) {
-
result.put("code", e.getMessage());
-
e.printStackTrace();
-
}
-
return result;
-
}
-
-
/**
-
* 执行编译获取方法的返回值
-
*
-
* @param compilePojo
-
* @return
-
*/
-
-
public Object executeMethod( CompilePojo compilePojo) {
-
JSONObject result = new JSONObject();
-
try {
-
Object returnData = compileService.executeMethod(compilePojo.getRuleName(), compilePojo.getJavaCode(),
-
compilePojo.getMethodName(), compilePojo.getParams());
-
result.put("code", "编译成功,内容是:" + returnData);
-
} catch (Exception e) {
-
result.put("code", e.getMessage());
-
e.printStackTrace();
-
}
-
return result;
-
}
-
}
测试项目
没有获取方法名
-
{
-
"ruleName":"TestScripts",
-
"javaCode":"package com.compile;public class TestScripts { public String hi(String name) { return \" hi, \" + name; } }"
-
}
返回结果

获取方法名
-
{
-
"ruleName":"TestScripts",
-
"javaCode":"package com.compile;public class TestScripts { public String hi(String name) { return \" hi, \" + name; } }",
-
"methodName":"hi",
-
"params":"wjx"
-
}
返回结果

打包部署测试
idea 右侧

springboot-java-compile 下面的 Lifecycle ,点击 package 打包

运行 jar
java -jar compile-web-0.0.1-SNAPSHOT.jar

继续测试,

发现报错了,好吧,只能调试程序了,
下面讲述一下 idea 如何调试 jar 包
java -jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 [jar 文件名]

会发现我们的项目在 5005 被监听
下面在 idea 添加远程调试 Remote

点击 Edit Configurations

以此点击左侧 + ,和下面 Remote 默认就 ok 了

项目里面打上断点,启动项目

控制台出现

继续测试,

发现
ToolProvider.getSystemJavaCompiler()获取的结果是null,查阅资料发现
上网搜了下,直接说就是找不到 jdk lib 目录下 tools.jar 文件,没法编译
查看 JAVA_HOME

解决方法就是 这个目录下面的 lib 文件夹的 tool.jar 拷贝到 jre1.8.0_191 下面就可以运行了

再次测试

到此已经完成了。
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
/**
* @author xm
* @version 1.0
* @date 2022/7/22 19:45
*/
@Slf4j
public class JavaUils {
private JavaUils() {
}
public static void main(String[] args) throws IOException {
ByteArrayOutputStream error = JavaUils.compileFile(javaList, compileExtJarNameSet);
if (error != null) {
log.error("compileFile fail. -->" + error);
}
// 导出jar包
File file = JavaUils.exportJar("jarName.jar");
JavaUils.deleteTempFile();
}
/**
* 编译文件生成路径
*/
private static final String COMPILE_JAVA_FILE_PATH = "." + File.separator + "compile_java_file" + File.separator;
private static final String COMPILE_LIB = "compile_lib/";
private static final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
public static final String JAVA_SUFFIX = ".java";
static {
log.info("---------javaCompiler------" + javaCompiler);
if (javaCompiler == null) {
// 本地cmd运行时ToolProvider.getSystemJavaCompiler()会获取不到javaCompiler,可以使用JavacTool.create()来创建
log.error("ToolProvider.getSystemJavaCompiler() is null.");
}
}
public static String packagePath(String fullPathClassName) {
if (fullPathClassName.endsWith(JAVA_SUFFIX)) {
return fullPathClassName.substring(0, fullPathClassName.lastIndexOf("."))
.substring(0, fullPathClassName.lastIndexOf("."))
.replace(".", File.separator) + File.separator;
}
return fullPathClassName.substring(0, fullPathClassName.lastIndexOf("."))
.replace(".", File.separator) + File.separator;
}
/**
* @param fullPathClassName com..dto.order.TestReq
* @return
*/
public static String fileName(String fullPathClassName) {
if (fullPathClassName.endsWith(JAVA_SUFFIX)) {
return fullPathClassName.substring(0, fullPathClassName.length() - JAVA_SUFFIX.length())
.substring(fullPathClassName.lastIndexOf(".") + 1) + JAVA_SUFFIX;
}
return fullPathClassName.substring(fullPathClassName.lastIndexOf(".") + 1) + JAVA_SUFFIX;
}
public static void validatedCompileFile(String fullPathClassName, String javaStr) {
int lastIndex = fullPathClassName.lastIndexOf(".");
if (lastIndex > 0) {
String packagePath = fullPathClassName.substring(0, lastIndex);
if (!javaStr.contains(packagePath)) {
throw new CompilationException("validated packagePath fail");
}
String className = fullPathClassName.substring(lastIndex + 1);
if (!javaStr.contains(className)) {
throw new CompilationException("validated className fail");
}
}
}
/**
* 编译指定的所有.java文件,编译后的文件
* @param collect 需要包含待编译的类文件.java及类的全路径类名
* @param copyJarNameSet 编译需要用到的第三方依赖jar。会去到springboot打包时默认的lib下获取,如需手动指定需要调整代码
* @return 为null时表示成功;非null时包含编译出错时的错误信息,可直接转为字符串打印
*/
public static ByteArrayOutputStream compileFile(Collection<JavaCompilerInfo> collect, Set<String> copyJarNameSet) throws IOException {
String loadJarParam = getLoadJarParam(copyJarNameSet);
String[] strings;
int i = 2;
if (loadJarParam != null && !loadJarParam.isEmpty()) {
strings = new String[collect.size() + 4];
strings[2] = "-classpath";
strings[3] = loadJarParam;
i = 4;
}else {
strings = new String[collect.size() + 2];
}
// 设置编码,避免编码问题
strings[0] = "-encoding";
strings[1] = "utf-8";
for (JavaCompilerInfo info : collect) {
File file = new File(COMPILE_JAVA_FILE_PATH + JavaUils.packagePath(info.getFullPathClassName()));
if (!file.exists()) {
file.mkdirs();
}
file = new File(file.getPath() + File.separator + JavaUils.fileName(info.getFullPathClassName()));
file.createNewFile();
FileUtils.write(file, info.getJavaStr(), StandardCharsets.UTF_8);
strings[i] = file.getPath();
i++;
}
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
int result = javaCompiler.run(null, null, byteOutputStream, strings);
if (result == 0) {
return null;
}
return byteOutputStream;
}
private static String getLoadJarParam(Set<String> copyJarNameSet) throws IOException {
if (copyJarNameSet == null || copyJarNameSet.isEmpty()) {
return null;
}
JarFile jarFile = getJarFile();
if (jarFile == null) {
return null;
}
StringBuilder builder = new StringBuilder();
for (String jarName : copyJarNameSet) {
if (jarName.trim().isEmpty()) {
continue;
}
String fileAbsolutePath = copyJarToCompileLib(jarFile, jarName);
if (fileAbsolutePath != null) {
builder.append(fileAbsolutePath);
}
}
String optionStr = builder.toString();
log.info("compiler option -classpath:{}", optionStr);
return optionStr;
}
private static String copyJarToCompileLib(JarFile jarFile , String jarName) {
try {
File destination = new File(COMPILE_LIB + jarName);
if (destination.exists() && destination.length() > 0) {
log.info("copyJarToCompileLib the jar file already exists. file.length():" + destination.length());
return destination.getAbsolutePath() + File.pathSeparator;
}
JarEntry jarEntry = jarFile.getJarEntry("BOOT-INF/lib/" + jarName);
if (jarEntry == null) {
log.error("jar file not found. jarName:{}", jarName);
return null;
}
InputStream inputStream = jarFile.getInputStream(jarEntry);
FileUtils.copyInputStreamToFile(inputStream, destination);
log.info("copyJarToCompileLib success. jarName:{}", jarName);
return destination.getAbsolutePath() + File.pathSeparator;
} catch (Exception e) {
log.error("copyJarToCompileLib fail. jarName:{}", jarName, e);
}
return null;
}
private static JarFile getJarFile() throws IOException {
String projectJarName = "springboot-test.jar";
File file = new File(projectJarName);
if (file.exists()) {
return new JarFile(file);
}
File file2 = new File("lib" + File.separator + projectJarName);
if (file2.exists()) {
return new JarFile(file2);
}
log.error("projectJar:{} not found", projectJarName);
return null;
}
public static boolean compileFile(String javaStr) {
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
int result = javaCompiler.run(null, null, byteOutputStream, javaStr);
if (result == 0) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
private static File getOutFile(String outPath) throws IOException {
File file = new File(outPath);
if (!file.exists()) {
file.createNewFile();
}
return file;
}
public static File exportJar(String outPath) throws IOException {
return exportJar(COMPILE_JAVA_FILE_PATH, outPath);
}
public static File exportJar(String packagePath, String outPath) throws IOException {
File outFile = getOutFile(outPath);
JarOutputStream out = new JarOutputStream(new FileOutputStream(outFile), getManifest());
createTempJarInner(out, new File(packagePath), "");
out.flush();
out.close();
return outFile;
}
/**
* @param out
* @param f 绝对路径 G:\git\compile\xm-compile\src\test\java\com\dto\OrderDto.java
* @param base 导出jar包里面此文件的路径
* @throws IOException
*/
private static void createTempJarInner(JarOutputStream out, File f, String base) throws IOException {
if (f.isDirectory()) {
File[] fl = f.listFiles();
if (fl != null && fl.length > 0) {
if (base.length() > 0) {
base = base + "/";
}
for (File file : fl) {
createTempJarInner(out, file, base + file.getName());
}
}
} else {
if (base.endsWith(".class")) {
toJar(out, f, base);
}
}
}
public static void deleteTempFile() throws IOException {
FileUtils.forceDelete(new File(COMPILE_JAVA_FILE_PATH));
}
private static void toJar(JarOutputStream out, File f, String base) throws IOException {
log.info("export to jar -->" + base);
out.putNextEntry(new JarEntry(base));
try(FileInputStream in = new FileInputStream(f)) {
byte[] buffer = new byte[1024];
int n = in.read(buffer);
while (n != -1) {
out.write(buffer, 0, n);
n = in.read(buffer);
}
}
}
private static Manifest getManifest() {
Manifest manifest = new Manifest();
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
//manifest.getMainAttributes().putValue("Main-Class", "Show");//指定Main Class
return manifest;
}
}
-
-
import java.io.BufferedReader;
-
import java.io.InputStream;
-
import java.io.InputStreamReader;
-
-
import javax.tools.JavaCompiler;
-
import javax.tools.ToolProvider;
-
-
import org.junit.Test;
-
import org.junit.runner.RunWith;
-
import org.springframework.boot.test.context.SpringBootTest;
-
import org.springframework.context.annotation.ComponentScan;
-
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-
import com.piao.main.ApplicationAdmin;
-
-
-
-
-
-
public class Compiler {
-
-
-
public void compiler1(){
-
String javaAbsolutePath = "D:/test/AlTest1.java";
-
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-
int status = compiler.run(null, null, null, "-encoding", "UTF-8", "-classpath", javaAbsolutePath.toString(), javaAbsolutePath);
-
if(status!=0){
-
System.out.println("没有编译成功!");
-
}
-
-
}
-
-
public void compiler2(){
-
String javaAbsolutePath = "D:/test/AlTest2.java";
-
-
try {
-
Process process = Runtime.getRuntime().exec("javac -classpath D:/test/ " + javaAbsolutePath);
-
InputStream errorStream = process.getErrorStream();
-
InputStreamReader inputStreamReader = new InputStreamReader(errorStream);
-
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
-
String line = null;
-
while ((line=bufferedReader.readLine()) != null){
-
System.out.println(line);
-
}
-
int exitVal = process.waitFor();
-
System.out.println("Process exitValue: " + exitVal);
-
} catch (Exception e) {
-
e.printStackTrace();
-
}
-
}
-
-
-
}
运行后, D:\test 目录会生成两个 class 文件
其中方法 compiler1 是使用 jdk 自带的 rt.jar 中的 javax.tools 包提供的编译器
方法 compiler2 是使用 Runtime 执行 javac 命令

浙公网安备 33010602011771号