groovy 模板引擎實現(xiàn)原理分析
groovy的SimpleTemplateEngine實現(xiàn)了模板功能,類似于jsp。那就分析groovy是如何實現(xiàn)模板的。
使用模板
Java代碼- 1.Template template = new SimpleTemplateEngine().createTemplate(
- 2. new StringReader("<% // This is a comment that will be filtered from output %>\n" +
- 3. "Hello <%out.println(name);%> !")
- 4. );
- 5.
- 6. final StringWriter sw = new StringWriter();
- 7. template.make([name:'bloodwolf_china').writeTo(sw);
- 8. println sw.toString();
- 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類
Java代碼- 1.public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
- 2. SimpleTemplate template = new SimpleTemplate();
- 3. String script = template.parse(reader);
- 4.
- 5. template.script = groovyShell.parse(script, "SimpleTemplateScript" + counter++ + ".groovy");
- 6.
- 7. return template;
- 8. }
- 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、創(chuàng)建了一個SimpleTemplate對象 2、解析模板,主要是把<%=exp%>轉(zhuǎn)為groovy的內(nèi)置表達式${exp},把非<%code%>轉(zhuǎn)為調(diào)用out.print(內(nèi)容)函數(shù),<%code%>中的就是groovy代碼了。這樣就把整個模板解析為一段代碼。如 引用 Hello <%out.println(name);%> ! 變成
Java代碼- 1.out.print("Hello ");
- 2.out.println(name);
- 3.out.print(" !");
- out.print("Hello ");
- out.println(name);
- out.print(" !");
復制代碼 3、用groovyShell獲取一個Script對象
Script對象只一個支持普通groovy對象,利用了Groovy的特性 實現(xiàn) getProperty(String property)方法,從參數(shù)綁定對象中獲取屬性,這樣腳本中就能獲取綁定參數(shù)。
Java代碼- 1.public abstract class Script extends GroovyObjectSupport {
- 2. private Binding binding;
- 3. public Object getProperty(String property) {
- 4. try {
- 5. return binding.getVariable(property);
- 6. } catch (MissingPropertyException e) {
- 7. return super.getProperty(property);
- 8. }
- 9. }
- 10. public abstract Object run();
- 11.}
- 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對象,然后調(diào)用GroovyClassLoader,CompilationUnit把CodeSource編譯成一個Script對象,run()方法中執(zhí)行的即是out.print(模板內(nèi)容)這段代碼。
在看看如何輸出模板內(nèi)容的
Java代碼- 1.private static class SimpleTemplate implements Template {
- 2.
- 3. protected Script script;
- 4.
- 5. public Writable make() {
- 6. return make(null);
- 7. }
- 8.
- 9. public Writable make(final Map map) {
- 10. return new Writable() {
- 11. /**
- 12. * Write the template document with the set binding applied to the writer.
- 13. *
- 14. * @see groovy.lang.Writable#writeTo(java.io.Writer)
- 15. */
- 16. public Writer writeTo(Writer writer) {
- 17. Binding binding;
- 18. if (map == null)
- 19. binding = new Binding();
- 20. else
- 21. binding = new Binding(map);
- 22. Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
- 23. PrintWriter pw = new PrintWriter(writer);
- 24. scriptObject.setProperty("out", pw);
- 25. scriptObject.run();
- 26. pw.flush();
- 27. return writer;
- 28. }
- 29.
- 30. /**
- 31. * Convert the template and binding into a result String.
- 32. *
- 33. * @see java.lang.Object#toString()
- 34. */
- 35. public String toString() {
- 36. StringWriter sw = new StringWriter();
- 37. writeTo(sw);
- 38. return sw.toString();
- 39. }
- 40. };
- 41. }
- 42.}
- 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();
- }
- };
- }
- }
復制代碼 很清楚了,調(diào)用make方法,創(chuàng)建一個Script對象,綁定參數(shù)binding = new Binding(map)。 創(chuàng)建一個PrintWriter,綁定為out參數(shù),而模板解析的代碼中的out.print(內(nèi)容)就有著落了。
所以:
?Groovy的模板是通過編譯,生成Java類,然后調(diào)用方法實現(xiàn)的 ?使用模板機制注意要緩存Script對象或Template對象,否則每次調(diào)用都會編譯生成一個新的Java類,導致內(nèi)存溢出/泄露
|