`
bloodwolf_china
  • 浏览: 129482 次
社区版块
存档分类
最新评论

groovy 模板引擎实现原理分析

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

使用模板
Template template = new SimpleTemplateEngine().createTemplate(
        new StringReader("<% // This is a comment that will be filtered from output %>\n" +
        "Hello <%out.println(name);%> !")
    );

    final StringWriter sw = new StringWriter();
    template.make([name:'bloodwolf_china').writeTo(sw);
    println sw.toString();


看看SimpleTemplateEngine类
public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
        SimpleTemplate template = new SimpleTemplate();
        String script = template.parse(reader);
              
            template.script = groovyShell.parse(script, "SimpleTemplateScript" + counter++ + ".groovy");
       
        return template;
    }


这儿做了三件事
1、创建了一个SimpleTemplate对象
2、解析模板,主要是把<%=exp%>转为groovy的内置表达式${exp},把非<%code%>转为调用out.print(内容)函数,<%code%>中的就是groovy代码了。这样就把整个模板解析为一段代码。如
引用
Hello <%out.println(name);%> !
变成
out.print("Hello ");
out.println(name);
out.print(" !");

3、用groovyShell获取一个Script对象

Script对象只一个支持普通groovy对象,利用了Groovy的特性
实现 getProperty(String property)方法,从参数绑定对象中获取属性,这样脚本中就能获取绑定参数。
public abstract class Script extends GroovyObjectSupport {
    private Binding binding;
    public Object getProperty(String property) {
        try {
            return binding.getVariable(property);
        } catch (MissingPropertyException e) {
            return super.getProperty(property);
        }
    }
     public abstract Object run();
}

groovyShell把一段代码组装成一个GroovyCodeSource对象,然后调用GroovyClassLoader,CompilationUnit把CodeSource编译成一个Script对象,run()方法中执行的即是out.print(模板内容)这段代码。

在看看如何输出模板内容的
private static class SimpleTemplate implements Template {

        protected Script script;

        public Writable make() {
            return make(null);
        }

        public Writable make(final Map map) {
            return new Writable() {
                /**
                 * Write the template document with the set binding applied to the writer.
                 *
                 * @see groovy.lang.Writable#writeTo(java.io.Writer)
                 */
                public Writer writeTo(Writer writer) {
                    Binding binding;
                    if (map == null)
                        binding = new Binding();
                    else
                        binding = new Binding(map);
                    Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
                    PrintWriter pw = new PrintWriter(writer);
                    scriptObject.setProperty("out", pw);
                    scriptObject.run();
                    pw.flush();
                    return writer;
                }

                /**
                 * Convert the template and binding into a result String.
                 *
                 * @see java.lang.Object#toString()
                 */
                public String toString() {
                    StringWriter sw = new StringWriter();
                    writeTo(sw);
                    return sw.toString();
                }
            };
        }
}

很清楚了,调用make方法,创建一个Script对象,绑定参数binding = new Binding(map)。
创建一个PrintWriter,绑定为out参数,而模板解析的代码中的out.print(内容)就有着落了。

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

相关推荐

Global site tag (gtag.js) - Google Analytics