首页 > 其他 > 详细

《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录

时间:2015-07-27 02:11:21      阅读:357      评论:0      收藏:0      [点我收藏+]
点滴悟透设计思想,Tiny模板引擎优化实录!
加入框架设计兴趣小组:http://bbs.tinygroup.org/group-113-1.html

?

Tiny模板引擎的实现方式原来是采用的编译方式,最近发生了一些问题,因此我觉得有必要把编译方式调整为解释方式,为此就开始了此次实现活动。

编译方式存在的问题

当时采用编译方式,主要是考虑到编译方式在运行时不必再去遍历语法树,因此就采用了编译方式。但是在实际应用当中,出现了如下问题:

文件路径冲突的问题

由于采用的是编译方式,这个时候就存在在一个选择,即:Java源代码落地或不落地的选择。如果Java文件不落地,则在有问题的时候,如果想要进行代码调试(虽然这种场景并不多见),那么就没有源代码可供调试。如果Java代码落地,则会存在一个问题,那就是资源文件在磁盘文件中产生冲突的问题。

同样的问题对于class文件也存在,如果不落地,那么每次应用重启动的时候,都要重新编译这些文件以产生class文件;如果落地,则也会产生冲突的问题。

当然,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

《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录

原文:http://j2eetop.iteye.com/blog/2230396

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!