《開源框架那點事兒25》:對框架模板引擎實現方式的改造實錄

點滴悟透設計思想,Tiny模板引擎優化實錄!
加入框架設計興趣小組: http://bbs.tinygroup.org/group-113-1.html

 

Tiny模板引擎的實現方式原來是採用的編譯方式,最近發生了一些問題,所以我以爲有必要把編譯方式調整爲解釋方式,爲此就開始了這次實現活動。html

編譯方式存在的問題

當時採用編譯方式,主要是考慮到編譯方式在運行時沒必要再去遍歷語法樹,所以就採用了編譯方式。可是在實際應用當中,出現了以下問題:java

文件路徑衝突的問題

因爲採用的是編譯方式,這個時候就存在在一個選擇,即:Java源代碼落地或不落地的選擇。若是Java文件不落地,則在有問題的時候,若是想要進行代碼調試(雖然這種場景並很少見),那麼就沒有源代碼可供調試。若是Java代碼落地,則會存在一個問題,那就是資源文件在磁盤文件中產生衝突的問題。算法

一樣的問題對於class文件也存在,若是不落地,那麼每次應用重啓動的時候,都要從新編譯這些文件以產生class文件;若是落地,則也會產生衝突的問題。express

固然,Tiny模板引擎經過增長一個配置項,解決了這個衝突的問題,可是因爲增長了一個配置項,從客觀上增長了維護人員的工做量,也容易形成當維護人員不瞭解這裏面的道道,忘記設置從而致使在一臺服務器中部署多個Tiny應用時多個應用中的模板文件生成的java文件和class文件的衝突,從而致使出現問題。數組

PermSize內存佔用問題

採用編譯方式的時候,因爲每一個模板文件都要生成一個類,每一個宏也要生成一個類,在宏調用過程當中,也要生成一些類。(原本是能夠不生成這些類的,可是因爲Tiny模板引擎支持了一些很是有用的特性,因此宏調用時時採用編譯方式,就要生成一些內嵌類來完成)。這樣,就會生成大量的Java類,從工程很是大的時候,就會致使PermSize打敗很是大。尤爲是在系統還在調試的時候,模板文件變化的時候,就要從新編譯生成新的類,爲了不必須從新啓動應用服務器才能生生效,所以採用了本身編寫ClassLoader的方式來達到即時刷新的問題,可是因爲Java的垃圾回收機制,決定了垃圾不是及時回收的,可是因爲每一個類都要有一個ClassLoader來支持,以便及時替換,所以這會進一步放大內存的佔用。性能優化

加載速度比較長的問題

因爲Tiny模板引擎中提供了宏,而這些宏能夠獨立存在,所以在應用啓動的時候就必須加載全部的宏到內存中,以便查找。因此就致使第一次啓動的時候,因爲要編譯全部的宏文件並加載之,致使啓動速度很是慢。在之後的啓動的時候,也必須檢測模板文件與生成的類是否一致,是否有被修改過,當a項目規模比較大的時候,這個時間也是比較長的。尤爲是在開發期,啓動時間增長10秒,都會讓開發人員感受到難以忍受。服務器

訪問速度的問題

採用編譯方式的問題,在訪問上也有一些問題。框架

爲了提高應用啓動時間,只有宏文件是在啓動時預選編譯好並加載了的,而模板文件和佈局文件則沒有這種待遇,這就致使若是在訪問的時候,第一次訪問的時候,須要編譯模板文件爲java文件,再把java文件編譯爲class文件,若是此次訪問還用到了佈局文件,還import了其它的模板文件,那麼悲劇了,第一個訪問者可能要多等待幾秒鐘的時間。同時,爲了不屢次編譯狀況的地生,還要增長同步鎖,這樣會進一步影響到訪問的效率。函數

具體尚未測試過ClassLoader太多對性能有多大的影響,可是毛估估是有必定影響的,畢竟要增長查找的層數。乾的活多了,乾的活慢了也是天然的,人是這樣,計算機也是一樣的道理。佈局

採用解釋方式帶來的好處

因爲採用解釋方式,所以沒必要生成java源文件和class文件,所以也就不存在文件路徑衝突的問題;一樣也不存在PermSize和衆多ClassLoader大量佔用內存的問題。

因爲採用解釋方式,第一次加載,只定性掃描部分關係的內容便可,所以掃描速度很是快;只有在直接執行的時候,才須要更詳細的處理,同時因爲不須要進行編譯,不須要作同步處理,所以加載速度會比編譯方式高許多,尤爲是和編譯方式的第一次加載時間相比。

訪問速度方面的問題,我原來的感受來講,感受編譯方式會快一些,畢竟它不用再雲遍歷語法樹,可是實際執行下來,感受解釋方式大體有一倍左右的提高,我分析了一下緣由,大體能夠認爲是以下緣由:1.因爲Java的優化策略,致使使用頻率高的訪問會進行深度性能優化,採用解釋方式,因爲用到的就是那幾個函數,所以能夠很快知足Java虛擬機的要求,更早的進行深度優化;2.因爲解釋方式和編譯方式相比,能夠採用更優化的解決方案,所以遍歷語法樹的時間由避免作一些事情彌補回來了,所以感覺性能反而更高一點點。總之,此次編譯改解釋,的效果仍是明顯的,各方面全面讓我滿意,尤爲是前面擔憂的執行效率方面也有大概50%左右的提高是讓我喜出望外的。還有一個意外之喜是經過把編譯方式改爲解釋執行方式,代碼規模縮小了近一半,由原來的8000+行,變成4000+行。同時,因爲沒必要要依賴JDT,antlr也只要依賴runtime包便可,還順便減小了3M的WAR包大小。

OK,說了這麼多,那就說說此次改造過程。

因爲團隊去島國旅遊,當時把這個任務交給一個留守同窗來完成,可是先後兩週的時候,沒有提交出我滿意的結果,因爲看不到後續完成的時間節點,沒有辦法,只好我老先生親自動手來完成了,OK開工,相信仔細閱讀下面一節內容的同窗,會對ANTLR解釋引擎的開發有深刻了解,甚至拿個人代碼照葫蘆畫瓢,直接就可用。

解釋引擎改造實錄

解釋引擎總控類

解釋引擎總控類是解釋引擎的核心,因爲這個東東是爲了Tiny模板引擎定製編寫的,所以若是有同窗要拿來改造,請照葫蘆畫瓢便可。因爲類不大,我就直接貼源碼上來,以便親們理解和我下面講解。

 

  1. public class TemplateInterpreter {  
  2.   
  3.     TerminalNodeProcessor[] terminalNodeProcessors = new TerminalNodeProcessor[200];  
  4.   
  5.     Map<Class<ParserRuleContext>, ContextProcessor> contextProcessorMap = new HashMap<Class<ParserRuleContext>, ContextProcessor>();  
  6.   
  7.     OtherTerminalNodeProcessor otherNodeProcessor = new OtherTerminalNodeProcessor();  
  8.   
  9.    
  10.   
  11.    
  12.   
  13.     public void addTerminalNodeProcessor(TerminalNodeProcessor processor) {  
  14.   
  15.         terminalNodeProcessors[processor.getType()] = processor;  
  16.   
  17.     }  
  18.   
  19.    
  20.   
  21.     public void addContextProcessor(ContextProcessor contextProcessor) {  
  22.   
  23.         contextProcessorMap.put(contextProcessor.getType(), contextProcessor);  
  24.   
  25.     }  
  26.   
  27.    
  28.   
  29.     public TinyTemplateParser.TemplateContext parserTemplateTree(String sourceName, String templateString) {  
  30.   
  31.         char[] source = templateString.toCharArray();  
  32.   
  33.         ANTLRInputStream is = new ANTLRInputStream(source, source.length);  
  34.   
  35.         // set source file name, it will be displayed in error report.  
  36.   
  37.         is.name = sourceName;  
  38.   
  39.         TinyTemplateParser parser = new TinyTemplateParser(new CommonTokenStream(new TinyTemplateLexer(is)));  
  40.   
  41.         return parser.template();  
  42.   
  43.     }  
  44.   
  45.    
  46.   
  47.     public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, String templateString, String sourceName, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {  
  48.   
  49.         interpret(engine, templateFromContext, parserTemplateTree(sourceName, templateString), pageContext, context, writer);  
  50.   
  51.         writer.flush();  
  52.   
  53.     }  
  54.   
  55.    
  56.   
  57.     public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, TinyTemplateParser.TemplateContext templateParseTree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {  
  58.   
  59.         for (int i = 0; i < templateParseTree.getChildCount(); i++) {  
  60.   
  61.             interpretTree(engine, templateFromContext, templateParseTree.getChild(i), pageContext, context, writer);  
  62.   
  63.         }  
  64.   
  65.     }  
  66.   
  67.    
  68.   
  69.     public Object interpretTree(TemplateEngineDefault engine, TemplateFromContext templateFromContext, ParseTree tree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {  
  70.   
  71.         Object returnValue = null;  
  72.   
  73.         if (tree instanceof TerminalNode) {  
  74.   
  75.             TerminalNode terminalNode = (TerminalNode) tree;  
  76.   
  77.             TerminalNodeProcessor processor = terminalNodeProcessors[terminalNode.getSymbol().getType()];  
  78.   
  79.             if (processor != null) {  
  80.   
  81.                 returnValue = processor.process(terminalNode, context, writer);  
  82.   
  83.             } else {  
  84.   
  85.                 returnValue = otherNodeProcessor.process(terminalNode, context, writer);  
  86.   
  87.             }  
  88.   
  89.         } else if (tree instanceof ParserRuleContext) {  
  90.   
  91.             ContextProcessor processor = contextProcessorMap.get(tree.getClass());  
  92.   
  93.             if (processor != null) {  
  94.   
  95.                 returnValue = processor.process(this, templateFromContext, (ParserRuleContext) tree, pageContext, context, engine, writer);  
  96.   
  97.             }  
  98.   
  99.             if (processor == null || processor != null && processor.processChildren()) {  
  100.   
  101.                 for (int i = 0; i < tree.getChildCount(); i++) {  
  102.   
  103.                     Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);  
  104.   
  105.                     if (value != null) {  
  106.   
  107.                         returnValue = value;  
  108.   
  109.                     }  
  110.   
  111.                 }  
  112.   
  113.             }  
  114.   
  115.    
  116.   
  117.         } else {  
  118.   
  119.             for (int i = 0; i < tree.getChildCount(); i++) {  
  120.   
  121.                 Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);  
  122.   
  123.                 if (returnValue == null && value != null) {  
  124.   
  125.                     returnValue = value;  
  126.   
  127.                 }  
  128.   
  129.             }  
  130.   
  131.         }  
  132.   
  133.         return returnValue;  
  134.   
  135.     }  
  136.   
  137.    
  138.   
  139.     public static void write(Writer writer, Object object) throws IOException {  
  140.   
  141.         if (object != null) {  
  142.   
  143.             writer.write(object.toString());  
  144.   
  145.             writer.flush();  
  146.   
  147.         }  
  148.   
  149.     }  
  150.   
  151. }  
public class TemplateInterpreter {

    TerminalNodeProcessor[] terminalNodeProcessors = new TerminalNodeProcessor[200];

    Map<Class<ParserRuleContext>, ContextProcessor> contextProcessorMap = new HashMap<Class<ParserRuleContext>, ContextProcessor>();

    OtherTerminalNodeProcessor otherNodeProcessor = new OtherTerminalNodeProcessor();

 

 

    public void addTerminalNodeProcessor(TerminalNodeProcessor processor) {

        terminalNodeProcessors[processor.getType()] = processor;

    }

 

    public void addContextProcessor(ContextProcessor contextProcessor) {

        contextProcessorMap.put(contextProcessor.getType(), contextProcessor);

    }

 

    public TinyTemplateParser.TemplateContext parserTemplateTree(String sourceName, String templateString) {

        char[] source = templateString.toCharArray();

        ANTLRInputStream is = new ANTLRInputStream(source, source.length);

        // set source file name, it will be displayed in error report.

        is.name = sourceName;

        TinyTemplateParser parser = new TinyTemplateParser(new CommonTokenStream(new TinyTemplateLexer(is)));

        return parser.template();

    }

 

    public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, String templateString, String sourceName, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {

        interpret(engine, templateFromContext, parserTemplateTree(sourceName, templateString), pageContext, context, writer);

        writer.flush();

    }

 

    public void interpret(TemplateEngineDefault engine, TemplateFromContext templateFromContext, TinyTemplateParser.TemplateContext templateParseTree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {

        for (int i = 0; i < templateParseTree.getChildCount(); i++) {

            interpretTree(engine, templateFromContext, templateParseTree.getChild(i), pageContext, context, writer);

        }

    }

 

    public Object interpretTree(TemplateEngineDefault engine, TemplateFromContext templateFromContext, ParseTree tree, TemplateContext pageContext, TemplateContext context, Writer writer) throws Exception {

        Object returnValue = null;

        if (tree instanceof TerminalNode) {

            TerminalNode terminalNode = (TerminalNode) tree;

            TerminalNodeProcessor processor = terminalNodeProcessors[terminalNode.getSymbol().getType()];

            if (processor != null) {

                returnValue = processor.process(terminalNode, context, writer);

            } else {

                returnValue = otherNodeProcessor.process(terminalNode, context, writer);

            }

        } else if (tree instanceof ParserRuleContext) {

            ContextProcessor processor = contextProcessorMap.get(tree.getClass());

            if (processor != null) {

                returnValue = processor.process(this, templateFromContext, (ParserRuleContext) tree, pageContext, context, engine, writer);

            }

            if (processor == null || processor != null && processor.processChildren()) {

                for (int i = 0; i < tree.getChildCount(); i++) {

                    Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);

                    if (value != null) {

                        returnValue = value;

                    }

                }

            }

 

        } else {

            for (int i = 0; i < tree.getChildCount(); i++) {

                Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer);

                if (returnValue == null && value != null) {

                    returnValue = value;

                }

            }

        }

        return returnValue;

    }

 

    public static void write(Writer writer, Object object) throws IOException {

        if (object != null) {

            writer.write(object.toString());

            writer.flush();

        }

    }

}

 

 
這個類,因此行數是80行,去掉15行的import和package,也就是65行而已,從類的職能來看,主要完成以下事宜: 
  1. 管理了TerminalNodeProcessor和ParserRuleContext
  2. parserTemplateTree:解析文本內容獲取語法樹
  3. interpret:解釋執行語法樹
  4. interpret:遍歷全部節點並解釋執行之
  5. interpretTree:若是是TerminalNode那麼找到合適的TerminalNode執行器去執行,若是找不到,則由OtherTerminalNodeProcessor去處理--實際上就是返回字符串了;若是是ParserRuleContext節點,那麼就由對應的執行器去執行,執行完了看看是否是要執行子節點,若是須要,那麼就繼續執行子節點,不然就返回。若是這兩種都不是,那就遍歷全部子節點去解釋執行了。

因此邏輯仍是比較清晰,最複雜的核心算法也只有30行,不論是什麼樣層級的同窗,看這些代碼都沒有任何難度了。

須要交待的一件事情是:爲何ContextProcessor的處理類是用Map保存的,而TerminalNodeProcessor則是用數組?這裏主要是爲了考慮到TerminalNode都有一個類型,用數據的方式速度更快一些。

上面說到有兩個接口,一個是處理TerminalNodeProcessor,另一個是處理ContextProcessor的,下面交待一下這兩個接口。

TerminalNodeProcessor

  1. public interface TerminalNodeProcessor<T extends ParseTree> {  
  2.   
  3.     int getType();  
  4.   
  5.     Object process(T parseTree, TemplateContext context, Writer writer) throws Exception;  
  6.   
  7. }  
public interface TerminalNodeProcessor<T extends ParseTree> {

    int getType();

    Object process(T parseTree, TemplateContext context, Writer writer) throws Exception;

}

 

getType:用於返回處理器可處理的類型,用於解釋引擎檢查是否是你的菜
  1. process:真正的處理邏輯實現的地方

ContextProcessor

  1. public interface ContextProcessor<T extends ParserRuleContext> {  
  2.   
  3.     Class<T> getType();  
  4.   
  5.    
  6.   
  7.     boolean processChildren();  
  8.   
  9.    
  10.   
  11.     Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, T parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception;  
  12.   
  13.    
  14.   
  15. }  
public interface ContextProcessor<T extends ParserRuleContext> {

    Class<T> getType();

 

    boolean processChildren();

 

    Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, T parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception;

 

}

 

  1. getType:用於返回處理器可處理的類型,用於解釋引擎檢查是否是你的菜
  2. processChildren:用於告訴引擎,你的兒子們是本身處理好了,仍是讓解釋引擎繼續執行。返回true表示讓引擎繼續處理
  3. process:真正的處理邏輯實現的地方

至此,整個解析引擎的框架就搭好了,剩下要作的就是去寫這些處理器了。

TerminalNodeProcessor實現類示例

其實這些實現類真的太簡單了,我都很差意思貼出來,爲了讓你們看明白,貼幾個說說意思就好 

DoubleNodeProcessor

  1. public class DoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {  
  2.   
  3.     public int getType() {  
  4.   
  5.         return TinyTemplateParser.FLOATING_POINT;  
  6.   
  7.     }  
  8.   
  9.    
  10.   
  11.     public boolean processChildren() {  
  12.   
  13.         return false;  
  14.   
  15.     }  
  16.   
  17.    
  18.   
  19.     public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {  
  20.   
  21.         String text=terminalNode.getText();  
  22.   
  23.         return Double.parseDouble(text);  
  24.   
  25.     }  
  26.   
  27. }  
public class DoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {

    public int getType() {

        return TinyTemplateParser.FLOATING_POINT;

    }

 

    public boolean processChildren() {

        return false;

    }

 

    public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {

        String text=terminalNode.getText();

        return Double.parseDouble(text);

    }

}

 

這貨的意思是:若是是Double類型的數據,就把字符串轉換成Double值返回。 

StringDoubleNodeProcessor

  1. public class StringDoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {  
  2.   
  3.     public int getType() {  
  4.   
  5.         return TinyTemplateParser.STRING_DOUBLE;  
  6.   
  7.     }  
  8.   
  9.     public boolean processChildren() {  
  10.   
  11.         return false;  
  12.   
  13.     }  
  14.   
  15.     public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {  
  16.   
  17.         String text=terminalNode.getText();  
  18.   
  19.         text=text.replaceAll("\\\\\"","\"");  
  20.   
  21.         text=text.replaceAll("[\\\\][\\\\]","\\\\");  
  22.   
  23.         return text.substring(1, text.length() - 1);  
  24.   
  25.     }  
  26.   
  27. }  
public class StringDoubleNodeProcessor implements TerminalNodeProcessor<TerminalNode> {

    public int getType() {

        return TinyTemplateParser.STRING_DOUBLE;

    }

    public boolean processChildren() {

        return false;

    }

    public Object process(TerminalNode terminalNode, TemplateContext context, Writer writer) {

        String text=terminalNode.getText();

        text=text.replaceAll("\\\\\"","\"");

        text=text.replaceAll("[\\\\][\\\\]","\\\\");

        return text.substring(1, text.length() - 1);

    }

}

這貨的意思是,若是是雙引號引住的字符串,那麼就把裏面的一些轉義字符處理掉,而後把外面的雙引號也去掉後返回。 

其它的和這個大同小異,總之很是簡單,想看的同窗能夠本身去看源碼,這裏就不貼了。

ContextProcessor類的實現示例

這裏面的處理,說實際的也沒有什麼複雜的,主要緣由是原來在寫模板引擎的時候,把運行時的一些東西,進行良好的抽象,所以這裏只是個簡單的調用而已。這裏貼2個稍微複雜的示範一下: 

ForProcessor

  1. public class ForProcessor implements ContextProcessor<TinyTemplateParser.For_directiveContext> {  
  2.   
  3.    
  4.   
  5.     public Class<TinyTemplateParser.For_directiveContext> getType() {  
  6.   
  7.         return TinyTemplateParser.For_directiveContext.class;  
  8.   
  9.     }  
  10.   
  11.     public boolean processChildren() {  
  12.   
  13.         return false;  
  14.   
  15.     }  
  16.   
  17.     public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.For_directiveContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {  
  18.   
  19.         String name = parseTree.for_expression().IDENTIFIER().getText();  
  20.   
  21.         Object values = interpreter.interpretTree(engine, templateFromContext, parseTree.for_expression().expression(),pageContext, context, writer);  
  22.   
  23.         ForIterator forIterator = new ForIterator(values);  
  24.   
  25.         context.put("$"+name + "For", forIterator);  
  26.   
  27.         boolean hasItem = false;  
  28.   
  29.         while (forIterator.hasNext()) {  
  30.   
  31.             TemplateContext forContext=new TemplateContextDefault();  
  32.   
  33.             forContext.setParent(context);  
  34.   
  35.             hasItem = true;  
  36.   
  37.             Object value = forIterator.next();  
  38.   
  39.             forContext.put(name, value);  
  40.   
  41.             try {  
  42.   
  43.                 interpreter.interpretTree(engine, templateFromContext, parseTree.block(),pageContext, forContext, writer);  
  44.   
  45.             } catch (ForBreakException be) {  
  46.   
  47.                 break;  
  48.   
  49.             } catch (ForContinueException ce) {  
  50.   
  51.                 continue;  
  52.   
  53.             }  
  54.   
  55.         }  
  56.   
  57.         if (!hasItem) {  
  58.   
  59.             TinyTemplateParser.Else_directiveContext elseDirectiveContext = parseTree.else_directive();  
  60.   
  61.             if (elseDirectiveContext != null) {  
  62.   
  63.                 interpreter.interpretTree(engine, templateFromContext, elseDirectiveContext.block(), pageContext,context, writer);  
  64.   
  65.             }  
  66.   
  67.         }  
  68.   
  69.         return null;  
  70.   
  71.     }  
  72.   
  73. }  
public class ForProcessor implements ContextProcessor<TinyTemplateParser.For_directiveContext> {

 

    public Class<TinyTemplateParser.For_directiveContext> getType() {

        return TinyTemplateParser.For_directiveContext.class;

    }

    public boolean processChildren() {

        return false;

    }

    public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.For_directiveContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {

        String name = parseTree.for_expression().IDENTIFIER().getText();

        Object values = interpreter.interpretTree(engine, templateFromContext, parseTree.for_expression().expression(),pageContext, context, writer);

        ForIterator forIterator = new ForIterator(values);

        context.put("$"+name + "For", forIterator);

        boolean hasItem = false;

        while (forIterator.hasNext()) {

            TemplateContext forContext=new TemplateContextDefault();

            forContext.setParent(context);

            hasItem = true;

            Object value = forIterator.next();

            forContext.put(name, value);

            try {

                interpreter.interpretTree(engine, templateFromContext, parseTree.block(),pageContext, forContext, writer);

            } catch (ForBreakException be) {

                break;

            } catch (ForContinueException ce) {

                continue;

            }

        }

        if (!hasItem) {

            TinyTemplateParser.Else_directiveContext elseDirectiveContext = parseTree.else_directive();

            if (elseDirectiveContext != null) {

                interpreter.interpretTree(engine, templateFromContext, elseDirectiveContext.block(), pageContext,context, writer);

            }

        }

        return null;

    }

}

這裏解釋一下它的執行邏輯: 

  1. 首先獲取循環變量名
  2. 接下來獲取要循環的對象
  3. 而後構建一個循環迭代器,並在上下文中放一個循環變量進去
  4. 而後真正執行循環,若是有在循環過程當中有break或continue指令,那麼就執行之
  5. 若是最後一個循環也沒有執行,那麼檢查 else 指令是否存在,若是存在就執行之

是否是很是簡單?

MapProcessor

  1. public class MapProcessor implements ContextProcessor<TinyTemplateParser.Expr_hash_mapContext> {  
  2.   
  3.     public Class<TinyTemplateParser.Expr_hash_mapContext> getType() {  
  4.   
  5.         return TinyTemplateParser.Expr_hash_mapContext.class;  
  6.   
  7.     }  
  8.   
  9.     public boolean processChildren() {  
  10.   
  11.         return false;  
  12.   
  13.     }  
  14.   
  15.     public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.Expr_hash_mapContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {  
  16.   
  17.         List<TinyTemplateParser.ExpressionContext> expressions = parseTree.hash_map_entry_list().expression();  
  18.   
  19.         List<TinyTemplateParser.ExpressionContext> expressionContexts = expressions;  
  20.   
  21.         Map<String, Object> map = new HashMap<String, Object>();  
  22.   
  23.         if (expressions != null) {  
  24.   
  25.             for (int i = 0; i < expressions.size(); i += 2) {  
  26.   
  27.                 String key = interpreter.interpretTree(engine, templateFromContext, expressions.get(i), pageContext,context, writer).toString();  
  28.   
  29.                 Object value = interpreter.interpretTree(engine, templateFromContext, expressions.get(i + 1),pageContext, context, writer);  
  30.   
  31.                 map.put(key, value);  
  32.   
  33.             }  
  34.   
  35.         }  
  36.   
  37.         return map;  
  38.   
  39.     }  
  40.   
  41. }  
public class MapProcessor implements ContextProcessor<TinyTemplateParser.Expr_hash_mapContext> {

    public Class<TinyTemplateParser.Expr_hash_mapContext> getType() {

        return TinyTemplateParser.Expr_hash_mapContext.class;

    }

    public boolean processChildren() {

        return false;

    }

    public Object process(TemplateInterpreter interpreter, TemplateFromContext templateFromContext, TinyTemplateParser.Expr_hash_mapContext parseTree, TemplateContext pageContext, TemplateContext context, TemplateEngineDefault engine, Writer writer) throws Exception {

        List<TinyTemplateParser.ExpressionContext> expressions = parseTree.hash_map_entry_list().expression();

        List<TinyTemplateParser.ExpressionContext> expressionContexts = expressions;

        Map<String, Object> map = new HashMap<String, Object>();

        if (expressions != null) {

            for (int i = 0; i < expressions.size(); i += 2) {

                String key = interpreter.interpretTree(engine, templateFromContext, expressions.get(i), pageContext,context, writer).toString();

                Object value = interpreter.interpretTree(engine, templateFromContext, expressions.get(i + 1),pageContext, context, writer);

                map.put(key, value);

            }

        }

        return map;

    }

}

 

 

這個是個構建MAP的處理器,它的執行邏輯是: 

  1. 新建個MAP對象,而後循環往MAP裏put數據便可以了。
  2. 最後返回map對象

我已經拿了最複雜的兩個來說了,其它的就更簡單了,所以就再也不貼了,關心的同窗們能夠去看源代碼。

總結

  1. 實際上用Java寫個新的語言啥的,沒有什麼難的,難的是你心頭的那種恐懼,畢竟如今的一些開源框架如Antlr等的支持下,作詞法分析,語法樹構建是很是容易的一件事情,只要規劃並定義好語法規則,後面的實現並無多複雜。
  2. 好的設計會讓你受益頗多,Tiny模板引擎由編譯換成解釋執行,沒有什麼傷筋動骨的變化,只是用新的方式實現了原有接口而已
  3. 對問題的分析的深刻程度決定了你代碼編寫的複雜程度,上次和一我的討論時有說過:之因此你寫不簡單,是由於你考慮得還不夠多,分析的還不夠細
  4. 至此這次重構完成,正在測試當中,將在近日推出。  

 


歡迎訪問開源技術社區:http://bbs.tinygroup.org。本例涉及的代碼和框架資料,將會在社區分享。《本身動手寫框架》成員QQ羣:228977971,一塊兒動手,瞭解開源框架的奧祕!或點擊加入QQ羣:http://jq.qq.com/?_wv=1027&k=d0myfX

相關文章
相關標籤/搜索