Java代碼生成利器之rapid-generate應用五

本文我們將分析一下rapid-generate源碼,看看其設計思路以及運行原理究竟是怎樣的。

我們從Test類的main函數入口說起

public static void main(String[]args)throws Exception{
            GeneratorFacade g = new GeneratorFacade();
             //刪除生成器的輸出目錄//
            g.deleteOutRootDir();

            //自動搜索數據庫中的所有表並生成文件,template爲模板的根目錄
            g.generateByAllTable("template");

        }

GeneratorFacade 類從命名上就能看出,運用了門面設計模式,該類作爲生成器功能的入口,統一組織管理各個模塊以及功能。

g.generateByAllTable(“template”);這一句實際上是調用GeneratorFacade 內部類ProcessUtils的processByAllTable方法。

public void generateByAllTable(String templateRootDir) throws Exception {
            new ProcessUtils().processByAllTable(templateRootDir,false);
        }

進一步分析processByAllTable()方法

public void processByAllTable(String templateRootDir,boolean isDelete) throws Exception {
    //該方法通過讀取數據庫的metaData,獲所有數據庫表信息,並在內存中生成對應的Table對象。主要應用底層接口java.sql.DatabaseMetaData
                List<Table> tables = TableFactory.getInstance().getAllTables();
                List exceptions = new ArrayList();
                for(int i = 0; i < tables.size(); i++ ) {
                    try {
                        processByTable(getGenerator(templateRootDir),tables.get(i),isDelete);
                    }catch(GeneratorException ge) {
                        exceptions.addAll(ge.getExceptions());
                    }
                }
                PrintUtils.printExceptionsSumary("",getGenerator(templateRootDir).getOutRootDir(),exceptions);
            }

得到list之後,接下來遍歷該集合,調用processByTable()方法

public void processByTable(Generator g, Table table,boolean isDelete) throws Exception {
    //生成GeneratorModel 對象,包含filePathModel和templateModel的兩個Map集合,爲下一步開始生成文件做準備
                GeneratorModel m = GeneratorModelUtils.newFromTable(table);
                PrintUtils.printBeginProcess(table.getSqlName()+" => "+table.getClassName(),isDelete);
                if(isDelete)
                    g.deleteBy(m.templateModel,m.filePathModel);
                else 
                    g.generateBy(m.templateModel,m.filePathModel);
            }     

讀取templateModel和filePathModel兩個Map集合作爲generateBy函數的參數

/**
         * 生成文件
         * @param templateModel 生成器模板可以引用的變量
         * @param filePathModel 文件路徑可以引用的變量
         * @throws Exception
         */
        public Generator generateBy(Map templateModel,Map filePathModel) throws Exception {
            processTemplateRootDirs(templateModel, filePathModel,false);
            return this;
        }

templateRootDirs是List集合,其中只有一個元素,[F:\2013-2014\Eclipse-J2EE\workspace\Rapid-Generator-Pro\template],該路徑正是java項目中template目錄所在路徑

這裏寫圖片描述

接着分析processTemplateRootDirs函數,由於list集合size位1,此處之循環1次,重點在scanTemplatesAndProcess函數。

private void processTemplateRootDirs(Map templateModel,Map filePathModel,boolean isDelete) throws Exception {
            if(StringHelper.isBlank(getOutRootDir())) throw new IllegalStateException("'outRootDir' property must be not null.");
            if(templateRootDirs.size() == 0) throw new IllegalStateException("'templateRootDirs' cannot empty");
            GeneratorException ge = new GeneratorException("generator occer error, Generator BeanInfo:"+BeanHelper.describe(this));
            for(int i = 0; i < this.templateRootDirs.size(); i++) {
                File templateRootDir = (File)templateRootDirs.get(i);
                List<Exception> exceptions = scanTemplatesAndProcess(templateRootDir,templateModel,filePathModel,isDelete);
                ge.addAll(exceptions); 
            }
            if(!ge.exceptions.isEmpty()) throw ge;
        }

scanTemplatesAndProcess函數接收template所在目錄的路徑以及templateModel和filePathModel兩個Map參數

        private List<Exception> scanTemplatesAndProcess(File templateRootDir, Map templateModel,Map filePathModel,boolean isDelete) throws Exception {
            if(templateRootDir == null) throw new IllegalStateException("'templateRootDir' must be not null");
            GLogger.println("-------------------load template from templateRootDir = '"+templateRootDir.getAbsolutePath()+"' outRootDir:"+new File(outRootDir).getAbsolutePath());
            //獲取模板根目錄下所有要生成的文件模板路徑
             List srcFiles = FileHelper.searchAllNotIgnoreFile(templateRootDir);

            List<Exception> exceptions = new ArrayList();
            for(int i = 0; i < srcFiles.size(); i++) {
                File srcFile = (File)srcFiles.get(i);
                try {
                    if(isDelete){
                        new TemplateProcessor().executeDelete(templateRootDir, templateModel,filePathModel, srcFile);
                    }else {
                        new TemplateProcessor().executeGenerate(templateRootDir, templateModel,filePathModel, srcFile);
                    }
                }catch(Exception e) {
                    if (ignoreTemplateGenerateException) {
                        GLogger.warn("iggnore generate error,template is:" + srcFile+" cause:"+e);
                        exceptions.add(e);
                    } else {
                        throw e;
                    }
                }
            }
            return exceptions;
        }

分析最終生成的核心代碼,調用底層的freemarker,將模板轉換爲對應源碼文件輸出到指定目錄下面。

    private void executeGenerate(File templateRootDir,Map templateModel, Map filePathModel ,File srcFile) throws SQLException, IOException,TemplateException {
                String templateFile = FileHelper.getRelativePath(templateRootDir, srcFile);
            //忽略目錄,以及非模板的文件 if(GeneratorHelper.isIgnoreTemplateProcess(srcFile, templateFile,includes,excludes)) {
                    return;
                }

                if(isCopyBinaryFile && FileHelper.isBinaryFile(srcFile)) {
                    String outputFilepath = proceeForOutputFilepath(filePathModel, templateFile);
                    GLogger.println("[copy binary file by extention] from:"+srcFile+" => "+new File(getOutRootDir(),outputFilepath));
                    IOHelper.copyAndClose(new FileInputStream(srcFile), new FileOutputStream(new File(getOutRootDir(),outputFilepath)));
                    return;
                }

                String outputFilepath = null;
                try {
                    outputFilepath = proceeForOutputFilepath(filePathModel,templateFile);

                    initGeneratorControlProperties(srcFile);
                    processTemplateForGeneratorControl(templateModel, templateFile);

                    if(gg.isIgnoreOutput()) {
                        GLogger.println("[not generate] by gg.isIgnoreOutput()=true on template:"+templateFile);
                        return;
                    }

                    if(outputFilepath != null ) {
                        generateNewFileOrInsertIntoFile(templateFile,outputFilepath, templateModel);
                    }
                }catch(Exception e) {
                    throw new RuntimeException("generate oucur error,templateFile is:" + templateFile+" => "+ outputFilepath+" cause:"+e, e);
                }
            }

最後附上在分析時所打的斷點

這裏寫圖片描述

總結,原理其實很簡單,就是用freemarker語法編寫template模板文件,生成時讀取數據庫信息,動態的將freemarker語法標記替換掉,最終就得到了源碼文件。

發佈了52 篇原創文章 · 獲贊 12 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章