在某些情況下,我們需要動態生成java代碼,通過動態編譯,然后執行代碼。JAVA API提供了相應的工具(JavaCompiler)來實現動態編譯。下面我們通過一個簡單的例子介紹,如何通過JavaCompiler實現java代碼動態編譯。
一、獲取JavaCompiler
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
- 1
獲取JDK提供的java編譯器,如果沒有提供編譯器,則返回null;
二、編譯
//獲取java文件管理類
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
//獲取java文件對象迭代器
Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(files);
//設置編譯參數
ArrayList<String> ops = new ArrayList<String>();
ops.add("-Xlint:unchecked");
//設置classpath
ops.add("-classpath");
ops.add(CLASS_PATH);
//獲取編譯任務
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, ops, null, it);
//執行編譯任務
task.call();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
當我們要編譯的源代碼中,引用了其他代碼,我們需要將引用代碼路徑設置到-classpath中,否則會編譯失敗。
三、執行
//要加載的類名
String className = "xxx.xxx.xxx";
//獲取類加載器
ClassLoader classLoader = XXX.class.getClassLoader();
//加載類
Class<?> cls = classLoader.loadClass(className);//調用方法名稱
String methodName = "execute";
//方法參數類型數組
Class<?>[] paramCls = {...};
//獲取方法
Method method = cls.getDeclaredMethod(methodName , paramCls);
//創建類實例
Object obj = cls.newInstance();
//方法參數
Object[] params = {...};
//調用方法
Object result = method.invoke(obj, params);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
四、完整代碼
//ClassUtil.java
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class ClassUtil {private static final Log logger = LogFactory.getLog(ClassUtil.class);private static JavaCompiler compiler;static{compiler = ToolProvider.getSystemJavaCompiler();}/*** 獲取java文件路徑* @param file* @return*/private static String getFilePath(String file){int last1 = file.lastIndexOf('/');int last2 = file.lastIndexOf('\\');return file.substring(0, last1>last2?last1:last2)+File.separatorChar;}/*** 編譯java文件* @param ops 編譯參數* @param files 編譯文件*/private static void javac(List<String> ops,String... files){StandardJavaFileManager manager = null;try{manager = compiler.getStandardFileManager(null, null, null);Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(files);JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, ops, null, it);task.call();if(logger.isDebugEnabled()){for(String file:files)logger.debug("Compile Java File:" + file);}}catch(Exception e){logger.error(e);}finally{if(manager!=null){try {manager.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 生成java文件* @param file 文件名* @param source java代碼* @throws Exception*/private static void writeJavaFile(String file,String source)throws Exception{if(logger.isDebugEnabled()){logger.debug("Write Java Source Code to:"+file);}BufferedWriter bw = null;try{File dir = new File(getFilePath(file));if(!dir.exists())dir.mkdirs();bw = new BufferedWriter(new FileWriter(file));bw.write(source);bw.flush();}catch(Exception e){throw e;}finally{if(bw!=null){bw.close();}}}/*** 加載類* @param name 類名* @return*/private static Class<?> load(String name){Class<?> cls = null;ClassLoader classLoader = null;try{classLoader = ClassUtil.class.getClassLoader();cls = classLoader.loadClass(name);if(logger.isDebugEnabled()){logger.debug("Load Class["+name+"] by "+classLoader);}}catch(Exception e){logger.error(e);}return cls;}/*** 編譯代碼并加載類* @param filePath java代碼路徑* @param source java代碼* @param clsName 類名* @param ops 編譯參數* @return*/public static Class<?> loadClass(String filePath,String source,String clsName,List<String> ops){try {writeJavaFile(CLASS_PATH+filePath,source);javac(ops,CLASS_PATH+filePath);return load(clsName);} catch (Exception e) {logger.error(e);}return null;}/*** 調用類方法* @param cls 類* @param methodName 方法名* @param paramsCls 方法參數類型* @param params 方法參數* @return*/public static Object invoke(Class<?> cls,String methodName,Class<?>[] paramsCls,Object[] params){Object result = null;try {Method method = cls.getDeclaredMethod(methodName, paramsCls);Object obj = cls.newInstance();result = method.invoke(obj, params);} catch (Exception e) {logger.error(e);}return result;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
五、測試
public class ClassUtilTest {private static final Log logger = LogFactory.getLog(ClassUtilTest.class);public static void main(String args[]){StringBuilder sb = new StringBuilder();sb.append("package com.even.test;");sb.append("import java.util.Map;\nimport java.text.DecimalFormat;\n");sb.append("public class Sum{\n");sb.append("private final DecimalFormat df = new DecimalFormat(\"#.#####\");\n");sb.append("public Double calculate(Map<String,Double> data){\n");sb.append("double d = (30*data.get(\"f1\") + 20*data.get(\"f2\") + 50*data.get(\"f3\"))/100;\n");sb.append("return Double.valueOf(df.format(d));}}\n");//設置編譯參數ArrayList<String> ops = new ArrayList<String>();ops.add("-Xlint:unchecked");//編譯代碼,返回classClass<?> cls = ClassUtil.loadClass("/com/even/test/Sum.java",sb.toString(),"com.even.test.Sum",ops);//準備測試數據Map<String,Double> data = new HashMap<String,Double>();data.put("f1", 10.0);data.put("f2", 20.0);data.put("f3", 30.0);//執行測試方法Object result = ClassUtil.invoke(cls, "calculate", new Class[]{Map.class}, new Object[]{data});//輸出結果logger.debug(data);logger.debug("(30*f1+20*f2+50*f3)/100 = "+result);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
測試結果
16:12:02.860 DEBUG com.even.tools.ClassUtil - Write Java Source Code to: .../classes//com/even/test/Sum.java
16:12:03.544 DEBUG com.even.tools.ClassUtil - Compile Java File:.../classes//com/even/test/Sum.java
16:12:03.545 DEBUG com.even.tools.ClassUtil - Load Class[com.even.test.Sum] by sun.misc.Launcher$AppClassLoader@73d16e93
16:12:03.547 DEBUG com.even.test.ClassUtilTest - {f1=10.0, f2=20.0, f3=30.0}
16:12:03.547 DEBUG com.even.test.ClassUtilTest - (30*f1+20*f2+50*f3)/100 = 22.0