Java 動態的建立注入程式碼,注入方法給類或者介面並通過反射呼叫
阿新 • • 發佈:2019-01-24
Java 動態拼接程式碼,將字串寫入檔案並編譯執行。
1 拼接程式碼並生成檔案
/* * 文 件 名: WriteJavaFile.java * 版 權: Sunny Technologies Co., Ltd. Copyright YYYY-YYYY, All rights reserved * 描 述: <描述> * 修 改 人: L.Hao * 修改時間: 2014-11-15 * 跟蹤單號: <跟蹤單號> * 修改單號: <修改單號> * 修改內容: <修改內容> */ package com.fms.xx.common; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; import org.apache.cxf.common.util.StringUtils; import com.fms.xx.model.ParamBean; /** * 寫Java檔案 * <功能詳細描述> * * @author L.Hao * @version [版本號, 2014-11-15] * @see [相關類/方法] * @since [產品/模組版本] */ public class WriteJavaFile { private static String cLibfilePath; //Clibray 路徑 private static String jnaUtilfilePath; //jnaUtil 路徑 private static String CHFile; //h 檔案路徑 private static String filePath; //類路徑 private static String targetDir; //編譯後classes路徑 private static String sourceDir;; //源目錄 /** * 生成並編譯檔案 * <功能詳細描述> * @param h_FilePath h檔案路徑 * @param projet_path 工程路徑 * @return [引數說明] * * @return boolean [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static boolean createFileAndCompiler(String dll_FilePath, String projet_path){ CHFile = dll_FilePath.replace(".dll", ".h"); //取得H檔案 List<FunctionParam> params = parseHfile(CHFile); //讀取H檔案 String methodName = params.get(0).getMethodName(); //方法名稱 initPath(dll_FilePath,projet_path,methodName); //初始化檔案路徑 //建立介面檔案 createFile(cLibfilePath); //讀介面CLibrary檔案 String cLibraryFileContent = readFile(cLibfilePath); //判斷檔案是否為空 if(StringUtils.isEmpty(cLibraryFileContent)){ String iniClibrayFileContent = spliceIniClibrayFileContent(methodName); intWriteStringToFile(cLibfilePath, iniClibrayFileContent); cLibraryFileContent = readFile(cLibfilePath); } //待拼接的內容 String appendContent = spliceCLibraryString(params); //寫檔案(如果已經存在就不拼接了) if(!cLibraryFileContent.trim().replaceAll("\\s*", "").contains(appendContent.trim().replaceAll("\\s*", ""))){ WriteStringToFile(cLibfilePath, cLibraryFileContent, appendContent); } //建立實現類檔案 createFile(jnaUtilfilePath); //讀介面JNAUtil檔案 String jNAUtilFileContent = readFile(jnaUtilfilePath); //判斷檔案是否為空 if(StringUtils.isEmpty(jNAUtilFileContent)){ String iniJnaUtilFileContent = spliceIniJnaUtilFileContent(methodName); intWriteStringToFile(jnaUtilfilePath, iniJnaUtilFileContent); jNAUtilFileContent = readFile(jnaUtilfilePath); } appendContent = spliceJNAUtilString(params); //寫檔案(如果已經存在就不拼接了) if(!jNAUtilFileContent.trim().replaceAll("\\s*", "").contains(appendContent.trim().replaceAll("\\s*", ""))){ WriteStringToFile(jnaUtilfilePath, jNAUtilFileContent, appendContent); } boolean result = compiler(filePath, sourceDir, targetDir); System.out.println("編譯返回Flag:"+result); //true 編譯成功 false//編譯失敗 return result; } /** <一句話功能簡述> * <功能詳細描述> * @param methodName //方法名稱 * @return [引數說明] * * @return String [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ private static String spliceIniClibrayFileContent(String methodName) { StringBuffer sb = new StringBuffer(" "); sb.append(" package com.fms.xx.common; ").append("\t\n");; sb.append(" import com.sun.jna.Library; ").append("\t\n");; sb.append(" public interface "+methodName+"CLibraryDynamic extends Library ").append("\t\n");; sb.append(" { ").append("\t\n");; sb.append(" } ").append("\t\n");; return sb.toString(); } /** <一句話功能簡述> * <功能詳細描述> * @param methodName //方法名稱 * @return [引數說明] * * @return String [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ private static String spliceIniJnaUtilFileContent(String methodName) { StringBuffer sb = new StringBuffer(" "); sb.append(" package com.fms.xx.common; ").append("\t\n");; sb.append(" ").append("\t\n");; sb.append(" import java.util.Arrays; ").append("\t\n");; sb.append(" import java.util.List; ").append("\t\n");; // sb.append(" import com.fms.xx.model.ParamBean; ").append("\t\n");; sb.append(" import com.sun.jna.Native; ").append("\t\n");; sb.append(" "); sb.append(" public class "+methodName+"JNAUtilDynamic ").append("\t\n");; sb.append(" { ").append("\t\n");; sb.append(" "); sb.append(" public String filePath ; ").append("\t\n");; sb.append(" ").append("\t\n");; sb.append(" public "+methodName+"JNAUtilDynamic(String filePath) ").append("\t\n");; sb.append(" { ").append("\t\n");; sb.append(" this.filePath = filePath; ").append("\t\n");; sb.append(" } ").append("\t\n");; sb.append(" ").append("\t\n");; sb.append(" } ").append("\t\n");; return sb.toString(); } /** 初始化檔案路徑 * <功能詳細描述> * @param h_FilePath * @param projet_path * @param methodName 方法名稱 * @param abFilePath [引數說明] * * @return void [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ private static void initPath(String h_FilePath, String projet_path, String methodName) { //當前ClassPath的絕對URI路徑。 String abFilePath = projet_path+"WEB-INF\\classes\\com\\fms\\xx\\common"; //com.fms.xx.common //生成檔案路徑 String createPath = projet_path+"WEB-INF\\classes"; System.out.println("Tomcat路徑:"+projet_path); //檔案和編譯路徑 cLibfilePath = abFilePath+"\\"+methodName+"CLibraryDynamic.java"; //介面類 jnaUtilfilePath = abFilePath+"\\"+methodName+"JNAUtilDynamic.java"; //呼叫類 System.out.println("當前建立檔案路徑:"+cLibfilePath); targetDir = createPath; sourceDir = abFilePath; filePath = abFilePath; } /** * 反射呼叫生成的檔案 * <功能詳細描述> * @param parames 引數列表 * @param i_arry 計算結果 * @param dllFilePath Dll檔案路徑 * @param methodName 被呼叫的方法名稱 * @return 計算結果 * * @return double[] [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static double[] reflectCall(List<String> parames, double[] i_arry ,String dllFilePath ,String methodName) { try { Constructor<?> conststr = Class.forName("com.fms.xx.common."+methodName+"JNAUtilDynamic") .getDeclaredConstructor(new Class[] { String.class }); conststr.setAccessible(true); Object bl = conststr.newInstance(new Object[] {dllFilePath}); //"F:\\soft\\apache-tomcat-7.0.52\\webapps\\xxcalculate\\upload\\141112212749\\SharedLib.dll" Class<? extends Object> clazz = bl.getClass(); Method method = clazz.getDeclaredMethod(methodName, List.class, double[].class); method.invoke(bl, parames, i_arry); System.out.println("計算結果:" + Arrays.toString(i_arry)); } catch (Exception e) { e.printStackTrace(); } return i_arry; } /** * 建立單個檔案 * <功能詳細描述> * @param filePath 檔案路徑 * @return [引數說明] * * @return boolean [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static boolean createFile(String filePath) { File file = new File(filePath); if (file.exists()) {// 判斷檔案是否存在 System.out.println("目標檔案已存在" + filePath); //file.delete(); return true; } if (filePath.endsWith(File.separator)) {// 判斷檔案是否為目錄 System.out.println("目標檔案不能為目錄!"); return false; } if (!file.getParentFile().exists()) {// 判斷目標檔案所在的目錄是否存在 // 如果目標檔案所在的資料夾不存在,則建立父資料夾 System.out.println("目標檔案所在目錄不存在,準備建立它!"); if (!file.getParentFile().mkdirs()) {// 判斷建立目錄是否成功 System.out.println("建立目標檔案所在的目錄失敗!"); return false; } } try { if (file.createNewFile()) {// 建立目標檔案 System.out.println("建立檔案成功:" + filePath); return true; } else { System.out.println("建立檔案失敗!"); return false; } } catch (IOException e) {// 捕獲異常 e.printStackTrace(); System.out.println("建立檔案失敗!" + e.getMessage()); return false; } } /** * 讀取指定檔案的內容 * 將指定檔案的內容讀入到StringBuffer中 * @param filePath * @return [引數說明] * * @return String [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static String readFile(String filePath) { File file = new File(filePath); BufferedReader reader = null; StringBuffer sb = new StringBuffer(); try { //System.out.println("以行為單位讀取檔案內容,一次讀一整行:"); reader = new BufferedReader(new FileReader(file)); String tempString = null; //int line = 1; // 一次讀入一行,直到讀入null為檔案結束 while ((tempString = reader.readLine()) != null) { sb.append(tempString).append("\t\n"); } reader.close(); } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } /** * 初始化字串 * <功能詳細描述> * @param filePath * @param content [引數說明] * * @return void [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static void intWriteStringToFile(String filePath,String content) { try { // 開啟一個寫檔案器,建構函式中的第二個引數true表示以追加形式寫檔案 FileWriter writer = new FileWriter(filePath, false); writer.write(content); writer.flush(); //重新整理檔案 writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 將字串寫入指定檔案 * 在已有檔案內容的基礎上寫入新內容 * @param filePath 檔案路徑 * @param oldContent 已有內容 * @param newContent 新加內容 * * @return void [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static void WriteStringToFile(String filePath, String oldContent, String newContent) { StringBuffer strBuffer = new StringBuffer(); try { oldContent = oldContent.substring(0, oldContent.lastIndexOf("}")); strBuffer.append(oldContent); strBuffer.append(newContent); strBuffer.append("}"); // 開啟一個寫檔案器,建構函式中的第二個引數true表示以追加形式寫檔案 FileWriter writer = new FileWriter(filePath, false); writer.write(strBuffer.toString()); writer.flush(); //重新整理檔案 writer.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 檔案編譯 * 呼叫java編譯器編譯java檔案 * @param filePath 檔案路徑 * @param sourceDir 原始檔所在目錄 * @param targetDir 目標檔案所在目錄 * * @return boolean [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ @SuppressWarnings("rawtypes") public static boolean compiler(String filePath, String sourceDir, String targetDir) { boolean compilerResult = false; try { DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); compilerResult = DynamicCompilerUtil.compiler(filePath, sourceDir, targetDir, diagnostics); if (compilerResult) { System.out.println("編譯成功"); } else { System.out.println("編譯失敗"); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { System.out.println(diagnostic.getMessage(null)); } } } catch (Exception e) { e.printStackTrace(); } return compilerResult; } /** * <一句話功能簡述> * <功能詳細描述> * @param fileName * @return [引數說明] * * @return List<FunctionParam> [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static List<FunctionParam> parseHfile(String fileName) { Map<String, List<FunctionParam>> map = ReadHFile.ReadHFile(fileName); List<FunctionParam> params = (List<FunctionParam>) map.get("params"); return params; } /** * 拼接介面檔案內容 * <功能詳細描述> * @param params * @return [引數說明] * * @return String [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static String spliceCLibraryString(List<FunctionParam> params) { StringBuffer strBuffer = new StringBuffer(); strBuffer.append(" public void "); strBuffer.append(params.get(0).getMethodName()).append("("); for (FunctionParam functionParam : params) { strBuffer.append(functionParam.getParamType().replace("char", "byte")) .append(" ") .append(functionParam.getParamName()) .append(","); } strBuffer.replace(strBuffer.length() - 1, strBuffer.length(), ""); strBuffer.append(");").append("\t\n"); return strBuffer.toString(); } /** * 拼接被呼叫類內容 * <功能詳細描述> * @param params * @return [引數說明] * * @return String [返回型別說明] * @exception throws [違例型別] [違例說明] * @see [類、類#方法、類#成員] */ public static String spliceJNAUtilString(List<FunctionParam> params) { String methodName = params.get(0).getMethodName(); StringBuffer strBuffer = new StringBuffer(); strBuffer.append("public double[] "); strBuffer.append(methodName) .append("(List<String> parameterList, double[] arr) throws Exception") .append("\t\n"); strBuffer.append("{").append("\t\n"); strBuffer.append(" "+methodName+"CLibraryDynamic INSTANCE = ("+methodName+"CLibraryDynamic)Native.loadLibrary(filePath,"+methodName+"CLibraryDynamic.class);") .append("\t\n"); strBuffer.append(" int num = 0;").append("\t\n"); strBuffer.append(" INSTANCE.").append(methodName).append("("); int count = params.size(); int num = 0; for (FunctionParam functionParam : params) { num++; if (count == num) { continue; } if ("double".equalsIgnoreCase(functionParam.getParamType())) { strBuffer.append(" Double.valueOf(parameterList.get(num++).toString()),") .append("\t\n"); } if ("char".equalsIgnoreCase(functionParam.getParamType())) { strBuffer.append(" parameterList.get(num++).toString().getBytes(\"GBK\"),") .append("\t\n"); } } strBuffer.append(" arr);").append("\t\n"); strBuffer.append(" return arr;").append("\t\n"); strBuffer.append("}").append("\t\n"); return strBuffer.toString(); } }
2 呼叫Java編譯器,編譯檔案生成二進位制碼
/* * 文 件 名: DynamicCompilerUtil.java * 版 權: Sunny Technologies Co., Ltd. Copyright YYYY-YYYY, All rights reserved * 描 述: <描述> * 修 改 人: L.Hao * 修改時間: 2014-11-15 * 跟蹤單號: <跟蹤單號> * 修改單號: <修改單號> * 修改內容: <修改內容> */ package com.fms.xx.common; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import javax.tools.JavaCompiler.CompilationTask; import org.apache.commons.lang.StringUtils; /** * <一句話功能簡述> * <功能詳細描述> * * @author L.Hao * @version [版本號, 2014-11-15] * @see [相關類/方法] * @since [產品/模組版本] */ @SuppressWarnings("all") public class DynamicCompilerUtil { /** * 編譯java檔案 * * @param filePath * 檔案或者目錄(若為目錄,自動遞迴編譯) * @param sourceDir * java原始檔存放目錄 * @param targetDir * 編譯後class類檔案存放目錄 * @param diagnostics * 存放編譯過程中的錯誤資訊 * @return * @throws Exception */ public static boolean compiler(String filePath, String sourceDir, String targetDir, DiagnosticCollector<JavaFileObject> diagnostics) throws Exception { // 獲取編譯器例項 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 獲取標準檔案管理器例項 StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); try { if (StringUtils.isEmpty(filePath) && StringUtils.isEmpty(sourceDir) && StringUtils.isEmpty(targetDir)) { return false; } // 得到filePath目錄下的所有java原始檔 File sourceFile = new File(filePath); List<File> sourceFileList = new ArrayList<File>(); getSourceFiles(sourceFile, sourceFileList); // 沒有java檔案,直接返回 if (sourceFileList.size() == 0) { System.out.println(filePath + "目錄下查詢不到任何java檔案"); return false; } // 獲取要編譯的編譯單元 Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(sourceFileList); /** * 編譯選項,在編譯java檔案時,編譯程式會自動的去尋找java檔案引用的其他的java原始檔或者class。 -sourcepath選項就是定義java原始檔的查詢目錄, -classpath選項就是定義class檔案的查詢目錄。 */ String location_jar = sourceDir.substring(0, sourceDir.indexOf("classes"))+"lib\\jna.jar"; System.out.println("編譯jar位置是:"+location_jar); Iterable<String> options = Arrays.asList("-d", targetDir, "-sourcepath", sourceDir,"-cp",location_jar); CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits); // 執行編譯任務 return compilationTask.call(); } finally { fileManager.close(); } } /** * 查詢該目錄下的所有的java檔案 * * @param sourceFile * @param sourceFileList * @throws Exception */ private static void getSourceFiles(File sourceFile, List<File> sourceFileList) throws Exception { if (sourceFile.exists() && sourceFileList != null) {// 檔案或者目錄必須存在 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(); return name.endsWith(".java") ? true : false; } } }); // 遞迴呼叫 for (File childFile : childrenFiles) { getSourceFiles(childFile, sourceFileList); } } else {// 若file物件為檔案 sourceFileList.add(sourceFile); } } } public static void main(String[] args) { try { // 編譯F:\\亞信工作\\SDL檔案\\sdl\\src目錄下的所有java檔案 String filePath = "F:\\WorkSpace\\xxcalculate\\src\\com\\test"; String sourceDir = "F:\\WorkSpace\\xxcalculate\\src\\com\\test"; String targetDir = "F:\\WorkSpace\\xxcalculate\\src\\com\\classes"; DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); boolean compilerResult = compiler(filePath, sourceDir, targetDir, diagnostics); if (compilerResult) { System.out.println("編譯成功"); } else { System.out.println("編譯失敗"); for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { // System.out.format("%s[line %d column %d]-->%s%n", diagnostic.getKind(), diagnostic.getLineNumber(), // diagnostic.getColumnNumber(), // diagnostic.getMessage(null)); System.out.println(diagnostic.getMessage(null)); } } } catch (Exception e) { e.printStackTrace(); } } }
3 呼叫例項
//開始建立jna呼叫介面和檔案 String realPath = ContextUtil.getServletContext().getRealPath("/"); // 獲取演算法詳細資訊物件 CaleArithmeticDetail caleArithmeticDetail = caclFormula.getCaleArithmeticDetail(); // 獲取標頭檔案引數列表 String dll_filePath = realPath + caleArithmeticDetail.getFilePath(); WriteJavaFile.createFileAndCompiler(dll_filePath,realPath);