groovy 模板引擎實現原理分析

groovy的SimpleTemplateEngine實現了模板功能,相似於jsp。那就分析groovy是如何實現模板的。 

使用模板  java

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;
    }
這兒作了三件事 
一、建立了一個SimpleTemplate對象 
二、解析模板,主要是把<%=exp%>轉爲groovy的內置表達式${exp},把非<%code%>轉爲調用out.print(內容)函數,<%code%>中的就是groovy代碼了。這樣就把整個模板解析爲一段代碼。如
引用
Hello <%out.println(name);%> !
變成 


out.print("Hello ");
out.println(name);
out.print(" !");
三、用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類,致使內存溢出/泄露
相關文章
相關標籤/搜索