groovy 模板引擎实现原理分析
groovy的SimpleTemplateEngine实现了模板功能,类似于jsp。那就分析groovy是如何实现模板的。
使用模板
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、创建了一个SimpleTemplate对象 2、解析模板,主要是把<%=exp%>转为groovy的内置表达式${exp},把非<%code%>转为调用out.print(内容)函数,<%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的特性 实现 getProperty(String property)方法,从参数绑定对象中获取属性,这样脚本中就能获取绑定参数。
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对象,然后调用GroovyClassLoader,CompilationUnit把CodeSource编译成一个Script对象,run()方法中执行的即是out.print(模板内容)这段代码。
在看看如何输出模板内容的
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();
- }
- };
- }
- }
复制代码 很清楚了,调用make方法,创建一个Script对象,绑定参数binding = new Binding(map)。 创建一个PrintWriter,绑定为out参数,而模板解析的代码中的out.print(内容)就有着落了。
所以:
?Groovy的模板是通过编译,生成Java类,然后调用方法实现的 ?使用模板机制注意要缓存Script对象或Template对象,否则每次调用都会编译生成一个新的Java类,导致内存溢出/泄露
|