1. 使用 sitemesh 的 Servlet Filter 做页面修饰.
这种方式是 sitemesh 默认的使用方式,我们先来分析一下工作流程.
Action的定义(webwork-default.xml):
代码
- <result-type name="freemarker" class="com.opensymphony.webwork.views.freemarker.FreemarkerResult" default="true"/>
- <action name="viewLogin" class="foo.bar.viewLoginAction">
- <result name="success">/login.ftlresult>
- 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
然后调用
代码
- RequestDispatcher dispatcher = context.getRequestDispatcher(decorator.getPage());
- 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文件的
部分,必须是写在文件中的,不能是include进来的..
-- 如果 decorator文件的部分是include进来的,则源.ftl中的中不能含有其他${xxx}
实现方法:
我们需要重载
com.opensymphony.webwork.views.freemarker.FreemarkerResult的doExecute方法,
在它执行 Template.process 之前, 让 sitemesh 为我们组合好新的模版, 然后狸猫换太子, 执行
MergedTemplate.process.
具体代码如下:
代码
- package com.simba.webwork.views.freemarker;
-
- import java.io.IOException;
- import java.io.StringReader;
- import java.io.StringWriter;
-
- import javax.servlet.http.HttpServletRequest;
-
- import com.opensymphony.module.sitemesh.Config;
- import com.opensymphony.module.sitemesh.Decorator;
- import com.opensymphony.module.sitemesh.Factory;
- import com.opensymphony.module.sitemesh.HTMLPage;
- import com.opensymphony.module.sitemesh.Page;
- import com.opensymphony.module.sitemesh.PageParser;
- import com.opensymphony.module.sitemesh.filter.TextEncoder;
- import com.opensymphony.webwork.ServletActionContext;
- import com.opensymphony.xwork.ActionInvocation;
-
- import freemarker.template.SimpleHash;
- import freemarker.template.Template;
- import freemarker.template.TemplateException;
- import freemarker.template.TemplateModel;
-
- /**
- *
- * @author simba
- *
- */
- public class SitemeshFreemarkerResult extends FreemarkerResult
- {
- private static final long serialVersionUID = 6889674369040918834L;
- private final static TextEncoder TEXT_ENCODER = new TextEncoder();
-
- public void doExecute(String location, ActionInvocation invocation) throws IOException,
- TemplateException
- {
- this.location = location;
- this.invocation = invocation;
- this.configuration = getConfiguration();
- this.wrapper = getObjectWrapper();
-
- Template template = configuration.getTemplate(location, deduceLocale());
- TemplateModel model = createModel();
-
- /*
- * 调用mergeTemplate得到组合后的模版...
- */
- Template mergedTemplate = mergeTemplate(template, model);
-
- /*
- * 执行mergedTemplate的处理
- */
-
- // Give subclasses a chance to hook into preprocessing
- if (preTemplateProcess(mergedTemplate, model))
- {
- try
- {
- // Process the template
- mergedTemplate.process(model, getWriter());
- }
- finally
- {
- // Give subclasses a chance to hook into postprocessing
- postTemplateProcess(mergedTemplate, model);
- }
- }
- }
-
- /**
- * Get decorator from sitemesh's factory according to specific request,
- * then get this decorator as a FreeMarker template.
- * By replace the "${body}" with "body template" (determined by action result),
- * we get the merged template which will handled by FreeMarkerResult.
- *
- * @param template
- * @return
- */
- private Template mergeTemplate(Template template, TemplateModel model)
- {
- HttpServletRequest request = ServletActionContext.getRequest();
-
- //创建sitemesh的Factory实例..
- Factory factory = Factory.getInstance(new Config(ServletActionContext.getServletConfig()));
-
- //Determine whether the given path should be excluded from decoration or not.
- if(factory.isPathExcluded(extractRequestPath(request)))
- return template;
-
- //传入request,sitemesh根据request在decorators.xml中寻找匹配的decorator
- Decorator decorator = factory.getDecoratorMapper().getDecorator(request, null);
-
- if (decorator == null)
- return template;
-
- try
- {
- //page是对action传进来的.ftl文件进行解析后得到的.
- Page page = parsePage(template, factory);
-
- SimpleHash hash = (SimpleHash) model;
-
- String title, body, head;
-
- if(page==null)
- {
- title="No Title";
- body="No Body";
- head="";
- }
- else
- {
- HTMLPage htmlPage = (HTMLPage)page;
-
- title=htmlPage.getTitle();
-
- StringWriter buffer = new StringWriter();
- htmlPage.writeBody(buffer);
- body=buffer.toString();
-
- buffer = new StringWriter();
- htmlPage.writeHead(buffer);
- head=buffer.toString();
-
- hash.put("page",htmlPage);
- }
-
- /*
- * 这里是为了能让include进来的.ftl模版使用${title},${head}和${base}标签,
- * 但是如果include进来的.ftl模版的<head>head>中又使用了FreeMarker的标签,
- * 比如${user.name},这个${user.name}就不会被解析了
- */
- hash.put("title",title);
- hash.put("head",head);
- hash.put("base",request.getContextPath());
-
- //将decorator所指向的文件,作为FreeMarker Template载入..
- Template decTemplate = configuration.getTemplate(decorator.getPage(), deduceLocale());
-
- String deTemplateString = decTemplate.toString();
- deTemplateStringdeTemplateString = deTemplateString.replace("${body}", body);
- deTemplateStringdeTemplateString = deTemplateString.replace("${title}", title);
- deTemplateStringdeTemplateString = deTemplateString.replace("${head}", head);
-
-
- return new Template(template.getName(), new StringReader(deTemplateString), configuration);
- }
- catch (IOException e)
- {
- e.printStackTrace();
- // log me ...
- return template;
- }
- }
-
- private Page parsePage(Template template, Factory factory) throws IOException
- {
- PageParser pageParser = factory.getPageParser(getContentType()!=null?getContentType():"text/html");
- return pageParser.parse(TEXT_ENCODER.encode((template.toString()).getBytes(), template.getEncoding()));
- }
-
- private String extractRequestPath(HttpServletRequest request)
- {
- String servletPath = request.getServletPath();
- String pathInfo = request.getPathInfo();
- String query = request.getQueryString();
- return (servletPath == null ? "" : servletPath) + (pathInfo == null ? "" : pathInfo)
- + (query == null ? "" : ("?" + query));
- }
-
- }
-
render_code();
只要在webwork-default.xml文件中指定 result-type为:
代码
- "freemarker" class="foo.bar.SitemeshFreemarkerResult" default="true"/>
render_code();
就可以了..
阅读(2468) | 评论(0) | 转发(0) |