webwork+FreeMarker+sitemesh的一种组合方法

1. 使用 sitemesh 的 Servlet Filter 做页面修饰.

这种方式是 sitemesh 默认的使用方式,我们先来分析一下工作流程.

Action的定义(webwork-default.xml):

代码
  1. <result-type name="freemarker" class="com.opensymphony.webwork.views.freemarker.FreemarkerResult" default="true"/>
  2. <action name="viewLogin" class="foo.bar.viewLoginAction">
  3. <result name="success">/login.ftl</result>
  4. </action>

render_code();

当客户端调用viewLogin,Web容器根据.action后缀,交给webwork的 ServletDispatcher 处理,
这个 viewLogin 会经过 ServletDispatcher->proxy.execute()->Action.execute()->executeResult() 等一系列处理,
因为 viewLogin 的 result-type 是 freemarker, 所以 executeResult() 会调用
com.opensymphony.webwork.views.freemarker.FreemarkerResult 来把 login.ftl 作为Template
写入 response.
FreemarkerResult()在创建FreeMarker Template的时候,会为它创建一个"加强版"的TemplateModel,包含以下对象:
* $stack = OgnlValueStack;
* $webwork = FreemarkerWebWorkUtil, a toolbox providing services like formatting url, accessing the value stack, etc;
* $name-of-property = property retrieved from the value stack.
* $Request = HttpServletRequest;
* $Session = HttpServletResponse;
* $Application = OgnlValueStack.
这个特性是webwork的FreemarkerResult为我们提供的..

这时,webwork的工作基本就结束了,接下来, sitemesh的PageFilter出场了...

PageFilter会根据request找到相应的 decorator, 我们假设它是 main.dec
然后调用

代码
  1. RequestDispatcher dispatcher = context.getRequestDispatcher(decorator.getPage());
  2. dispatcher.include(request, response);

render_code();

因为decorator文件是 .dec 后缀, 而web.xml中映射 .dec 到
com.opensymphony.module.sitemesh.freemarker.FreemarkerDecoratorServlet

所以,此时 FreemarkerDecoratorServlet 接管 .dec 文件, 把 reponse(对应 login.ftl ) Include
到 .dec 的reponse...至此,页面的组合工作就完成了...
不过这次, FreeMarker就没有那么好的运气了, 它的TemplateModel不再是"加强版"的~~
decorator文件中所包含的其他 .ftl 模版, 是不能够使用 $stack, $webwork 等对象的...

总结一下:
粗略的流程是这样的
webwork --> FreemarkerResult --> Sitemesh --> FreeMarker

其中,FreeMarker被调用2次(影响系统性能) ,并且第二次被调用时,其TemplateModel不具备webwork特性(即,非加强版)...

2. 在webwork中使用sitemesh

我们理想中的流程应该是这样的:

webwork --> Sitemesh --> FreemarkerResult

webwork执行Action,执行完毕之后,找到对应的result模版,把这个模版交给sitemesh去修饰,组合成一个新的模版,
再把这个新的模版交给 FreemarkerResult 处理. FreemarkerResult 解析组合后的模版文件并写入response.
所谓"交给sitemesh去修饰",只是调用一下sitemesh的Factory得到其配置文件中所对应的decorator文件,组合模版的工作
还是要我们来做...

这样一来,我们就不再需要 sitemesh 的 Filter, 也不再需要 FreemarkerDecoratorServlet.与第一种方案相比,显然会提升系统性能..

并且更重要的是,这个组合后的模版所对应的TemplateModel是加强版的,decorator中所包含的其他 .ftl 文件,
也同样可以使用 $stack, $webwork 等对象.

此方案的限制条件:
-- decorator文件必须是Freemarker模版,不能用JSP等其他文件...
-- decorator文件的<head></head>部分,必须是写在文件中的,不能是include进来的..
-- 如果 decorator文件的<head></head>部分是include进来的,则源.ftl中的<head></head>中不能含有其他${xxx}

实现方法:

我们需要重载
com.opensymphony.webwork.views.freemarker.FreemarkerResult的doExecute方法,
在它执行 Template.process 之前, 让 sitemesh 为我们组合好新的模版, 然后狸猫换太子, 执行
MergedTemplate.process.

具体代码如下:

代码
  1. package com.simba.webwork.views.freemarker;
  2. import java.io.IOException;
  3. import java.io.StringReader;
  4. import java.io.StringWriter;
  5. import javax.servlet.http.HttpServletRequest;
  6. import com.opensymphony.module.sitemesh.Config;
  7. import com.opensymphony.module.sitemesh.Decorator;
  8. import com.opensymphony.module.sitemesh.Factory;
  9. import com.opensymphony.module.sitemesh.HTMLPage;
  10. import com.opensymphony.module.sitemesh.Page;
  11. import com.opensymphony.module.sitemesh.PageParser;
  12. import com.opensymphony.module.sitemesh.filter.TextEncoder;
  13. import com.opensymphony.webwork.ServletActionContext;
  14. import com.opensymphony.xwork.ActionInvocation;
  15. import freemarker.template.SimpleHash;
  16. import freemarker.template.Template;
  17. import freemarker.template.TemplateException;
  18. import freemarker.template.TemplateModel;
  19. /**
  20. *
  21. * @author simba
  22. *
  23. */
  24. public class SitemeshFreemarkerResult extends FreemarkerResult
  25. {
  26. private static final long serialVersionUID = 6889674369040918834L;
  27. private final static TextEncoder TEXT_ENCODER = new TextEncoder();
  28. public void doExecute(String location, ActionInvocation invocation) throws IOException,
  29. TemplateException
  30. {
  31. this.location = location;
  32. this.invocation = invocation;
  33. this.configuration = getConfiguration();
  34. this.wrapper = getObjectWrapper();
  35. Template template = configuration.getTemplate(location, deduceLocale());
  36. TemplateModel model = createModel();
  37. /*
  38. * 调用mergeTemplate得到组合后的模版...
  39. */
  40. Template mergedTemplate = mergeTemplate(template, model);
  41. /*
  42. * 执行mergedTemplate的处理
  43. */
  44. // Give subclasses a chance to hook into preprocessing
  45. if (preTemplateProcess(mergedTemplate, model))
  46. {
  47. try
  48. {
  49. // Process the template
  50. mergedTemplate.process(model, getWriter());
  51. }
  52. finally
  53. {
  54. // Give subclasses a chance to hook into postprocessing
  55. postTemplateProcess(mergedTemplate, model);
  56. }
  57. }
  58. }
  59. /**
  60. * Get decorator from sitemesh's factory according to specific request,
  61. * then get this decorator as a FreeMarker template.
  62. * By replace the "${body}" with "body template" (determined by action result),
  63. * we get the merged template which will handled by FreeMarkerResult.
  64. *
  65. * @param template
  66. * @return
  67. */
  68. private Template mergeTemplate(Template template, TemplateModel model)
  69. {
  70. HttpServletRequest request = ServletActionContext.getRequest();
  71. //创建sitemesh的Factory实例..
  72. Factory factory = Factory.getInstance(new Config(ServletActionContext.getServletConfig()));
  73. //Determine whether the given path should be excluded from decoration or not.
  74. if(factory.isPathExcluded(extractRequestPath(request)))
  75. return template;
  76. //传入request,sitemesh根据request在decorators.xml中寻找匹配的decorator
  77. Decorator decorator = factory.getDecoratorMapper().getDecorator(request, null);
  78. if (decorator == null)
  79. return template;
  80. try
  81. {
  82. //page是对action传进来的.ftl文件进行解析后得到的.
  83. Page page = parsePage(template, factory);
  84. SimpleHash hash = (SimpleHash) model;
  85. String title, body, head;
  86. if(page==null)
  87. {
  88. title="No Title";
  89. body="No Body";
  90. head="<!-- No head -->";
  91. }
  92. else
  93. {
  94. HTMLPage htmlPage = (HTMLPage)page;
  95. title=htmlPage.getTitle();
  96. StringWriter buffer = new StringWriter();
  97. htmlPage.writeBody(buffer);
  98. body=buffer.toString();
  99. buffer = new StringWriter();
  100. htmlPage.writeHead(buffer);
  101. head=buffer.toString();
  102. hash.put("page",htmlPage);
  103. }
  104. /*
  105. * 这里是为了能让include进来的.ftl模版使用${title},${head}和${base}标签,
  106. * 但是如果include进来的.ftl模版的<head></head>中又使用了FreeMarker的标签,
  107. * 比如${user.name},这个${user.name}就不会被解析了
  108. */
  109. hash.put("title",title);
  110. hash.put("head",head);
  111. hash.put("base",request.getContextPath());
  112. //将decorator所指向的文件,作为FreeMarker Template载入..
  113. Template decTemplate = configuration.getTemplate(decorator.getPage(), deduceLocale());
  114. String deTemplateString = decTemplate.toString();
  115. deTemplateStringdeTemplateString = deTemplateString.replace("${body}", body);
  116. deTemplateStringdeTemplateString = deTemplateString.replace("${title}", title);
  117. deTemplateStringdeTemplateString = deTemplateString.replace("${head}", head);
  118. return new Template(template.getName(), new StringReader(deTemplateString), configuration);
  119. }
  120. catch (IOException e)
  121. {
  122. e.printStackTrace();
  123. // log me ...
  124. return template;
  125. }
  126. }
  127. private Page parsePage(Template template, Factory factory) throws IOException
  128. {
  129. PageParser pageParser = factory.getPageParser(getContentType()!=null?getContentType():"text/html");
  130. return pageParser.parse(TEXT_ENCODER.encode((template.toString()).getBytes(), template.getEncoding()));
  131. }
  132. private String extractRequestPath(HttpServletRequest request)
  133. {
  134. String servletPath = request.getServletPath();
  135. String pathInfo = request.getPathInfo();
  136. String query = request.getQueryString();
  137. return (servletPath == null ? "" : servletPath) + (pathInfo == null ? "" : pathInfo)
  138. + (query == null ? "" : ("?" + query));
  139. }
  140. }

render_code();

只要在webwork-default.xml文件中指定 result-type为:

代码
  1. <result-type name="freemarker" class="foo.bar.SitemeshFreemarkerResult" default="true"/>

render_code();
就可以了..

相关资料:

webwork+FreeMarker+sitemesh的一种组合方法来源网络,如有侵权请告知,即处理!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值