分享

groovy 模板引擎实现原理分析

 CevenCheng 2012-11-25

groovy 模板引擎实现原理分析








groovy的SimpleTemplateEngine实现了模板功能,类似于jsp。那就分析groovy是如何实现模板的。 

使用模板 

Java代码
  1. 1.Template template = new SimpleTemplateEngine().createTemplate(   
  2. 2.        new StringReader("<% // This is a comment that will be filtered from output %>\n" +   
  3. 3.        "Hello <%out.println(name);%> !")   
  4. 4.    );   
  5. 5.  
  6. 6.    final StringWriter sw = new StringWriter();   
  7. 7.    template.make([name:'bloodwolf_china').writeTo(sw);   
  8. 8.    println sw.toString();  
  9. Template template = new SimpleTemplateEngine().createTemplate(
  10.         new StringReader("<% // This is a comment that will be filtered from output %>\n" +
  11.         "Hello <%out.println(name);%> !")
  12.     );

  13.     final StringWriter sw = new StringWriter();
  14.     template.make([name:'bloodwolf_china').writeTo(sw);
  15.     println sw.toString();
复制代码
看看SimpleTemplateEngine类 

Java代码
  1. 1.public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {   
  2. 2.        SimpleTemplate template = new SimpleTemplate();   
  3. 3.        String script = template.parse(reader);   
  4. 4.                 
  5. 5.            template.script = groovyShell.parse(script, "SimpleTemplateScript" + counter++ + ".groovy");   
  6. 6.          
  7. 7.        return template;   
  8. 8.    }  
  9. public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
  10.         SimpleTemplate template = new SimpleTemplate();
  11.         String script = template.parse(reader);
  12.               
  13.             template.script = groovyShell.parse(script, "SimpleTemplateScript" + counter++ + ".groovy");
  14.        
  15.         return template;
  16.     }
复制代码
这儿做了三件事 
1、创建了一个SimpleTemplate对象 
2、解析模板,主要是把<%=exp%>转为groovy的内置表达式${exp},把非<%code%>转为调用out.print(内容)函数,<%code%>中的就是groovy代码了。这样就把整个模板解析为一段代码。如
引用
Hello <%out.println(name);%> !
变成 

Java代码
  1. 1.out.print("Hello ");   
  2. 2.out.println(name);   
  3. 3.out.print(" !");  
  4. out.print("Hello ");
  5. out.println(name);
  6. out.print(" !");
复制代码
3、用groovyShell获取一个Script对象 

Script对象只一个支持普通groovy对象,利用了Groovy的特性 
实现 getProperty(String property)方法,从参数绑定对象中获取属性,这样脚本中就能获取绑定参数。 

Java代码
  1. 1.public abstract class Script extends GroovyObjectSupport {   
  2. 2.    private Binding binding;   
  3. 3.    public Object getProperty(String property) {   
  4. 4.        try {   
  5. 5.            return binding.getVariable(property);   
  6. 6.        } catch (MissingPropertyException e) {   
  7. 7.            return super.getProperty(property);   
  8. 8.        }   
  9. 9.    }   
  10. 10.     public abstract Object run();   
  11. 11.}  
  12. public abstract class Script extends GroovyObjectSupport {
  13.     private Binding binding;
  14.     public Object getProperty(String property) {
  15.         try {
  16.             return binding.getVariable(property);
  17.         } catch (MissingPropertyException e) {
  18.             return super.getProperty(property);
  19.         }
  20.     }
  21.      public abstract Object run();
  22. }
复制代码
groovyShell把一段代码组装成一个GroovyCodeSource对象,然后调用GroovyClassLoader,CompilationUnit把CodeSource编译成一个Script对象,run()方法中执行的即是out.print(模板内容)这段代码。 

在看看如何输出模板内容的 

Java代码
  1. 1.private static class SimpleTemplate implements Template {   
  2. 2.  
  3. 3.        protected Script script;   
  4. 4.  
  5. 5.        public Writable make() {   
  6. 6.            return make(null);   
  7. 7.        }   
  8. 8.  
  9. 9.        public Writable make(final Map map) {   
  10. 10.            return new Writable() {   
  11. 11.                /**  
  12. 12.                 * Write the template document with the set binding applied to the writer.  
  13. 13.                 *  
  14. 14.                 * @see groovy.lang.Writable#writeTo(java.io.Writer)  
  15. 15.                 */  
  16. 16.                public Writer writeTo(Writer writer) {   
  17. 17.                    Binding binding;   
  18. 18.                    if (map == null)   
  19. 19.                        binding = new Binding();   
  20. 20.                    else  
  21. 21.                        binding = new Binding(map);   
  22. 22.                    Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);   
  23. 23.                    PrintWriter pw = new PrintWriter(writer);   
  24. 24.                    scriptObject.setProperty("out", pw);   
  25. 25.                    scriptObject.run();   
  26. 26.                    pw.flush();   
  27. 27.                    return writer;   
  28. 28.                }   
  29. 29.  
  30. 30.                /**  
  31. 31.                 * Convert the template and binding into a result String.  
  32. 32.                 *  
  33. 33.                 * @see java.lang.Object#toString()  
  34. 34.                 */  
  35. 35.                public String toString() {   
  36. 36.                    StringWriter sw = new StringWriter();   
  37. 37.                    writeTo(sw);   
  38. 38.                    return sw.toString();   
  39. 39.                }   
  40. 40.            };   
  41. 41.        }   
  42. 42.}  
  43. private static class SimpleTemplate implements Template {

  44.         protected Script script;

  45.         public Writable make() {
  46.             return make(null);
  47.         }

  48.         public Writable make(final Map map) {
  49.             return new Writable() {
  50.                 /**
  51.                  * Write the template document with the set binding applied to the writer.
  52.                  *
  53.                  * @see groovy.lang.Writable#writeTo(java.io.Writer)
  54.                  */
  55.                 public Writer writeTo(Writer writer) {
  56.                     Binding binding;
  57.                     if (map == null)
  58.                         binding = new Binding();
  59.                     else
  60.                         binding = new Binding(map);
  61.                     Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
  62.                     PrintWriter pw = new PrintWriter(writer);
  63.                     scriptObject.setProperty("out", pw);
  64.                     scriptObject.run();
  65.                     pw.flush();
  66.                     return writer;
  67.                 }

  68.                 /**
  69.                  * Convert the template and binding into a result String.
  70.                  *
  71.                  * @see java.lang.Object#toString()
  72.                  */
  73.                 public String toString() {
  74.                     StringWriter sw = new StringWriter();
  75.                     writeTo(sw);
  76.                     return sw.toString();
  77.                 }
  78.             };
  79.         }
  80. }
复制代码
很清楚了,调用make方法,创建一个Script对象,绑定参数binding = new Binding(map)。 
创建一个PrintWriter,绑定为out参数,而模板解析的代码中的out.print(内容)就有着落了。 

所以: 

?Groovy的模板是通过编译,生成Java类,然后调用方法实现的 
?使用模板机制注意要缓存Script对象或Template对象,否则每次调用都会编译生成一个新的Java类,导致内存溢出/泄露

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多