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;
}
}
java动态编译及生成jar包
最新推荐文章于 2024-04-30 17:28:01 发布