《开源框架那点事儿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

相关文章
相关标签/搜索