免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2517 | 回复: 1
打印 上一主题 下一主题

groovy 模板引擎实现原理分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-01-19 17:10 |只看该作者 |倒序浏览
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类,导致内存溢出/泄露

论坛徽章:
5
丑牛
日期:2014-01-21 08:26:26卯兔
日期:2014-03-11 06:37:43天秤座
日期:2014-03-25 08:52:52寅虎
日期:2014-04-19 11:39:48午马
日期:2014-08-06 03:56:58
2 [报告]
发表于 2012-01-19 20:08 |只看该作者
这个思路呵呵,牛!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP