1. 程式人生 > >Java 動態的建立注入程式碼,注入方法給類或者介面並通過反射呼叫

Java 動態的建立注入程式碼,注入方法給類或者介面並通過反射呼叫

     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);