struts2 是支持 velocity 的,所以不需要什么大规模更改.大多就是测试的时候按提示添加缺少的 class.这里让它支持 velocity 的布局功能
首先在项目中新建个类,路径如下.这个类改写了 struts2 里的 velocity 类
package struts2.velocity; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import javax.servlet.Servlet; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspFactory; import javax.servlet.jsp.PageContext; import org.apache.struts2.ServletActionContext; import org.apache.struts2.dispatcher.VelocityResult; import org.apache.struts2.views.JspSupportServlet; import org.apache.struts2.views.velocity.VelocityManager; import org.apache.velocity.Template; import org.apache.velocity.context.Context; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; public class VelocityLayoutResult extends VelocityResult { private static final long serialVersionUID = 6020934292083047099L; private static final Logger LOG = LoggerFactory .getLogger(VelocityLayoutResult.class); public static String KEY_SCREEN_CONTENT = "screen_content"; public static String KEY_LAYOUT = "layout"; public static final String PROPERTY_DEFAULT_LAYOUT = "tools.view.servlet.layout.default.template"; public static final String PROPERTY_LAYOUT_DIR = "tools.view.servlet.layout.directory"; public static final String PROPERTY_INPUT_ENCODING = "input.encoding"; public static final String PROPERTY_OUTPUT_ENCODING = "output.encoding"; public static final String PROPERTY_CONTENT_TYPE = "default.contentType"; protected VelocityManager velocityManager; protected String defaultLayout; protected String layoutDir; protected String inputEncoding; protected String outputEncoding; protected String contentType; @Override public void setVelocityManager(VelocityManager mgr) { this.velocityManager = mgr; super.setVelocityManager(mgr); } @Override public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { ValueStack stack = ActionContext.getContext().getValueStack(); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); JspFactory jspFactory = null; ServletContext servletContext = ServletActionContext .getServletContext(); Servlet servlet = JspSupportServlet.jspSupportServlet; velocityManager.init(servletContext); boolean usedJspFactory = false; PageContext pageContext = (PageContext) ActionContext.getContext().get( ServletActionContext.PAGE_CONTEXT); if (pageContext == null && servlet != null) { jspFactory = JspFactory.getDefaultFactory(); pageContext = jspFactory.getPageContext(servlet, request, response, null, true, 8192, true); ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, pageContext); usedJspFactory = true; } try { String encoding = getEncoding(finalLocation); String contentType = getContentType(finalLocation); if (encoding != null) { contentType = contentType + ";charset=" + encoding; } Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation, encoding); Context context = createContext(velocityManager, stack, request, response, finalLocation); StringWriter stringWriter = new StringWriter(); t.merge(context, stringWriter); context.put(KEY_SCREEN_CONTENT, stringWriter.toString()); Object obj = context.get(KEY_LAYOUT); String layout = (obj == null) ? null : obj.toString(); defaultLayout = (String) velocityManager.getVelocityEngine() .getProperty(PROPERTY_DEFAULT_LAYOUT); layoutDir = (String) request.getAttribute("layoutDir"); //System.out.print(layoutDir); if(layoutDir ==""){ layoutDir = (String) velocityManager.getVelocityEngine() .getProperty(PROPERTY_LAYOUT_DIR); } if (!layoutDir.endsWith("/")) { layoutDir += '/'; } if (!layoutDir.startsWith("/")) { layoutDir = "/" + layoutDir; } defaultLayout = layoutDir + defaultLayout; if (layout == null) { layout = defaultLayout; } else { if(layout.startsWith("@/")){ layout = layout.replace("@/", "/"); }else{ layout = layoutDir + layout; } } Template layoutVm = null; try { inputEncoding = (String) velocityManager.getVelocityEngine() .getProperty(PROPERTY_INPUT_ENCODING); layoutVm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, layout, inputEncoding); } catch (Exception e) { LOG.error("VelocityLayoutResult: Can't load layout /"" + layout + "/": " + e); if (!layout.equals(defaultLayout)) { layoutVm = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, defaultLayout, inputEncoding); } } outputEncoding = (String) velocityManager.getVelocityEngine() .getProperty(PROPERTY_OUTPUT_ENCODING); Writer writer = new OutputStreamWriter(response.getOutputStream(), outputEncoding); response.setContentType(contentType); layoutVm.merge(context, writer); writer.flush(); } catch (Exception e) { LOG.error("Unable to render Velocity Template, '" + finalLocation + "'", e); throw e; } finally { if (usedJspFactory) { jspFactory.releasePageContext(pageContext); } } return; } }
WEB-INF下新建个文件.命名:velocity.properties,内容如下(当然可以自己再加别的),用来存放velocity相关的一些信息:
tools.view.servlet.layout.directory =/template/ tools.view.servlet.layout.default.template=defalut.vm input.encoding=UTF-8 output.encoding=UTF-8
tools.view.servlet.layout.directory =/template/ 这行假定所有的 .vm 模板文件都在根目录的 /template 文件夹下,下面的login.vm和blank.vm 都是在这个目录下.
接着配置 struts.xml,在 struts 根节点下加入:
<constant name="struts.velocity.configfile" value="/WEB-INF/velocity.properties"/>
还是 struts.xml 下面这个配置用来输出,注意黑体 :
<package name="default" namespace="/" extends="struts-default"> <result-types> <result-type name="velocity" class="struts2.velocity.VelocityLayoutResult" /> </result-types> <action name="login" class="com.exdoit.Main" method="login"> <result type="velocity ">/template/login.vm </result> </action> </package>
这个是 login.vm 的代码 #set($layout = "blank.vm") 用来设置布局模板 .
#set($layout = "blank.vm") <ul> <li><span>帐号:</span><input type="text" name="name" id="loginForm_name" /><span><a href="" target="_blank">用户注册</a></span></li> <li><span>密码:</span><input type="password" name="pw" id="loginForm_pw" /><span><a href="" target="_blank">忘记密码</a></span></li> <li><input type="button" value="登录" id="loginForm_logonbtn" /><input type="button" value="取消" id="loginForm_cancelbtn" /></li> </ul>
这个是 blank.vm 的代码 ${screen_content} 就是要替换成 login.vm 的内容
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>${page_title}</title> <link media="screen,print" rel="stylesheet" href="/common/css/inc.css" type="text/css" charset="utf-8" /> <script type="text/javascript" src="/common/JS/jquery/jquery.js" charset="UTF-8"></script> <script type="text/javascript" src="/common/JS/common.js"></script> </head> <body> ${screen_content} </body> </html>
运行测试...
注,struts2.velocity.VelocityLayoutResult 这个类是Google来的.当时忘了记源地址.对原作者表示感谢...
我修改的原版的其中几行.用来设置模板路径这些问题.毕竟有可能不是所有的 vm 文件都一定在 velocity.properties 指定的目录下.
当路径 @/ 开头时,如:#set($layout = "@/other/layout/blank.vm") 表示这路径是从根目录开始.无视 velocity.properties 的路径配置.
在当前项目配置中假如:
#set($layout = "@/other/layout/blank.vm") == /other/layout/blank.vm
#set($layout = "other/layout/blank.vm") == /template /other/layout/blank.vm
在action 中设置 request.setAttribute("layoutDir", "/abc/") ,可以指定result 里vm的路径.无视 velocity.properties 的路径配置,注意这个是仅这个 action 的输出结果会换路径,其它action不受影响 .
在当前项目配置中假如:
#set($layout = "@/other/layout/blank.vm") == /other/layout/blank.vm
#set($layout = "other/layout/blank.vm") == /abc /other/layout/blank.vm