Java_动态编译总结
不多说直接上代码:
动态编译的主类:
package com.lkb.autoCode.util;
import com.lkb.autoCode.constant.AutoCodeConstant;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.tools.*;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* AutoCompiler 动态编译器
*
* @author Lilin
* @date 2016/5/18
*/
public class AutoCompiler {
private static final Log log = LogFactory.getLog(AutoCompiler.class);
/**
* Description: Compile java source file to java class with run method
*
* @param fullFileName the java source file name with full path
* @return true-compile successfully, false - compile unsuccessfully
*/
public static boolean compileFile(String fullFileName) {
boolean bRet = false;
// get compiler
JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();
// compile the java source code by run method
int iCompileRet = oJavaCompiler.run(null, null, null, fullFileName);
// set compile result
if (0 == iCompileRet) {
bRet = true;
}
return bRet;
}
/**
* Description: Compile java source file to java class with getTask
* method, it can specify the class output path and catch diagnostic
* information
*
* @param fullFileName the java source file name with full path
* @param outputPath the output path of java class file
* @return true-compile successfully, false - compile unsuccessfully
* @throws IOException
*/
public static boolean compileFile(String fullFileName, String outputPath) {
System.out.println("源文件路径:" + fullFileName);
System.out.println("输入路径:" + outputPath);
boolean bRet = false;
// get compiler
// TODO 当运行环境是JRE是无法直接ToolProvider.getSystemJavaCompiler();这么获取
JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler();
// define the diagnostic object, which will be used to save the diagnostic information
DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new DiagnosticCollector<JavaFileObject>();
// get StandardJavaFileManager object, and set the diagnostic for the object
StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler
.getStandardFileManager(oDiagnosticCollector, null, null);
// set class output location
JavaFileManager.Location oLocation = StandardLocation.CLASS_OUTPUT;
try {
File outputFile = new File(outputPath);
if (!outputFile.exists()) {
outputFile.mkdir();
}
List<File> dependencies = new ArrayList<File>();
// 加载依赖的jar文件
dependencies.addAll(getJarFiles(AutoCodeConstant.JARS_PATH));
// 加载依赖的class文件
dependencies.add(new File(AutoCodeConstant.BASE_PATH));
oStandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, dependencies);
oStandardJavaFileManager.setLocation(oLocation, Arrays
.asList(new File[]{outputFile}));
File sourceFile = new File(fullFileName);
// get JavaFileObject object, it will specify the java source file.
Iterable<? extends JavaFileObject> oItJavaFileObject = oStandardJavaFileManager
.getJavaFileObjectsFromFiles(Arrays.asList(sourceFile));
// -g 生成所有调试信息
// -g:none 不生成任何调试信息
// -g:{lines,vars,source} 只生成某些调试信息
// -nowarn 不生成任何警告
// -verbose 输出有关编译器正在执行的操作的消息
// -deprecation 输出使用已过时的 API 的源位置
// -classpath <路径> 指定查找用户类文件的位置
// -cp <路径> 指定查找用户类文件的位置
// -sourcepath <路径> 指定查找输入源文件的位置
// -bootclasspath <路径> 覆盖引导类文件的位置
// -extdirs <目录> 覆盖安装的扩展目录的位置
// -endorseddirs <目录> 覆盖签名的标准路径的位置
// -d <目录> 指定存放生成的类文件的位置
// -encoding <编码> 指定源文件使用的字符编码
// -source <版本> 提供与指定版本的源兼容性
// -target <版本> 生成特定 VM 版本的类文件
// -version 版本信息
// -help 输出标准选项的提要
// -X 输出非标准选项的提要
// -J<标志> 直接将 <标志> 传递给运行时系统
// 编译选项,将编译产生的类文件放在当前目录下
//Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING, "-classpath", getJarFiles(AutoCodeConstant.JARS_PATH));
Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING);
// compile the java source code by using CompilationTask's call method
bRet = oJavaCompiler.getTask(null, oStandardJavaFileManager,
oDiagnosticCollector, options, null, oItJavaFileObject).call();
//print the Diagnostic's information
for (Diagnostic oDiagnostic : oDiagnosticCollector
.getDiagnostics()) {
// log.info("Error on line: "
// + oDiagnostic.getLineNumber() + "; URI: "
// + oDiagnostic.getSource().toString());
System.out.printf(
"Code: %s%n" +
"Kind: %s%n" +
"Position: %s%n" +
"Start Position: %s%n" +
"End Position: %s%n" +
"Source: %s%n" +
"Message: %s%n",
oDiagnostic.getCode(), oDiagnostic.getKind(),
oDiagnostic.getPosition(), oDiagnostic.getStartPosition(),
oDiagnostic.getEndPosition(), oDiagnostic.getSource(),
oDiagnostic.getMessage(null));
}
} catch (IOException e) {
log.error("动态编译出现异常", e);
} finally {
//close file manager
if (null != oStandardJavaFileManager) {
try {
oStandardJavaFileManager.close();
} catch (IOException e) {
e.printStackTrace();
log.error("动态编译关闭文件管理器出现异常", e);
}
}
}
return bRet;
}
/**
* 查找该目录下的所有的jar文件
*
* @param jarPath
* @throws Exception
*/
private static List<File> getJarFiles(String jarPath) {
final List<File> dependencyJars = new ArrayList<File>();
File sourceFile = new File(jarPath);
if (sourceFile.exists()) {// 文件或者目录必须存在
if (sourceFile.isDirectory()) {// 若file对象为目录
// 得到该目录下以.java结尾的文件或者目录
File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
String name = pathname.getName();
if (name.endsWith(".jar") ? true : false) {
//jars[0] = jars[0] + pathname.getPath() + ";";
dependencyJars.add(pathname);
return true;
}
return false;
}
}
});
}
}
return dependencyJars;
}
}
辅助类:
package com.lkb.autoCode.constant;
/**
* AutoCodeConstant
*
* @author Lilin
* @date 2016/5/18
*/
public class AutoCodeConstant {
/**
* 登录模板名称
*/
public static String LOGIN_TEMPLATE_FTL = "LoginClientTemplate.ftl";
/**
* 项目根路径
*/
public final static String BASE_PATH = AutoCodeConstant.class.getResource("/").toString().replaceAll("^file:/", "");
/**
* Jar包所在目录
*/
public final static String JARS_PATH = BASE_PATH.replace("classes","lib");
/**
* 模板根路径
*/
public final static String BASE_TEMPLATE_PATH = BASE_PATH + "template";
/**
* client目录
*/
public final static String LOCAL_CLIENT_BASE_PATH = "src/main/java/com/lkb/sb/client/";
/**
* client目录
*/
public final static String CLIENT_BASE_PATH = "com/lkb/sb/client/";
/**
* client类名后缀
*/
public final static String LOGIN_CLIENT_SUFFIX = "LoginClient.java";
/**
* 默认编译输出路径
*/
public final static String DEFULT_OUTPUT_PATH = "/";
/**
* 编码格式
*/
public final static String CONTENT_ENCODING = "UTF-8";
}
开发背景:需求是根据代码模板动态生成java代码,并动态编译
开发过程中遇到的阻塞:模板代码中有依赖别的class文件和jar文件无法加载的问题
解决方法:
// 加载依赖的jar文件
dependencies.addAll(getJarFiles("jar文件的根路径"));
// 加载依赖的class文件
dependencies.add(new File("class文件的根路径"));
/**
* 查找该目录下的所有的jar文件
*
* @param jarPath
* @throws Exception
*/
private static List<File> getJarFiles(String jarPath) {
final List<File> dependencyJars = new ArrayList<File>();
File sourceFile = new File(jarPath);
if (sourceFile.exists()) {// 文件或者目录必须存在
if (sourceFile.isDirectory()) {// 若file对象为目录
// 得到该目录下以.java结尾的文件或者目录
File[] childrenFiles = sourceFile.listFiles(new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
} else {
String name = pathname.getName();
if (name.endsWith(".jar") ? true : false) {
//jars[0] = jars[0] + pathname.getPath() + ";";
dependencyJars.add(pathname);
return true;
}
return false;
}
}
});
}
}
return dependencyJars;
}