`

进一步提升 Struts 2 对 Velocity 的支持力度

阅读更多

级别: 中级

刘 冬 (javayou@gmail.com), 开发工程师,  

2007 年 7 月 30 日

本文主要介绍如何解决 Struts 2 和最新版本的 Velocity 结合的问题以及进一步提升 Struts 2 对 Velocity 的支持力度。
<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->

Struts 2 是 Struts 框架的一个全新版本,它废弃了原有 Struts 1.x 的框架结构,在 WebWork 的基础上全面提升了代码的配置灵活性、可测试性以及超强的扩展性。Struts 2 更是提供了对 Velocity 和 FreeMarker 模板引擎的支持,这破天荒的举动大大的方便了 Velocity 和 FreeMarker 的开发者,从而也更有效的推动了模板技术的发展。

在进行 Struts 2 和 Velocity 结合的试验中发现下面几个问题:

  1. 与 VelocityTools 1.3 存在兼容性问题;
  2. 不支持 Velocity Layout;
  3. 处理 velocity 模板时存在编码问题;

好在由于 Struts 2 的超强扩展性,使得这三个问题的解决变得非常简单,而且我们无需修改 Struts 2 的源码,下面分别给出三个问题的解决办法。

第一个问题:与 VelocityTools 1.3 存在兼容性问题

这个问题是因为 VelocityTools 这个项目从 1.2 升级到 1.3 时修改了一些类的方法(Method)导致的。在 1.3 这个版本中 ToolboxManager 类的 getToolboxContext 方法改名为 getToolbox,因此在启动程序的时候就会报 getToolboxContext 方法没找到的异常。

Struts 2 是在 VelocityManager 类的 createContext 方法中调用 ToolboxManager 的,而 Struts 2 允许通过配置来修改 VelocityManager 的实现类,因此我们只需要从 VelocityManager 继承一个子类,并重写 createContext 方法即可。重写的代码很简单,直接从 VelocityManager 的 createContext 方法中拷贝代码然后将 getToolboxContext 改为 getToolbox。

接着需要修改 Struts 2 的配置来启用这个新的子类,配置如下(struts.xml):

<constant name="struts.velocity.manager.classname" value="struts2.VelocityFixedManager"/>

或者是 struts.properties:

struts.velocity.manager.classname = struts2.VelocityFixedManager





回页首


第二个问题:不支持 Velocity Layout

首先简单的介绍一下 Velocity 的 Layout 技术。Velocity 的 Layout 是通过 VelocityTools 这个项目来实现的。大多数网站上每个页面基本上都会遵循相同的排版,例如一个页面由 banner 条、内容和底部版权说明组成。那么对于该网站上的每个网页,我们都需要编写重复的代码来显示 banner 条以及底部版权说明。而 Velocity 的 Layout 技术可以让我们把页面的布局和页面的内容分开到不同 velocity 模板文件中。要使用 Layout 技术只需要把 VelocityViewServlet 换成 VelocityLayoutServlet 即可。VelocityLayoutServlet 内置了一个变量 $screen_content,它首先执行内容模板页面并把执行后的字符串结果存入 $screen_content 变量中,然后再去执行布局页面,这样就相当于把内容页面嵌入到布局页面。

关于 ResultType

Struts 2 中把各种各样的视图例如 JSP、Velocity、FreeMarker 等等都封装成 ResultType 的子类,也就是说每一种视图 Struts 2 都有与之对应的 ResultType 的实现类, 本文中 Velocity 视图对应的类是 VelocityResult

但是这个问题在 Struts 2 环境中就要麻烦很多。因为 Struts 2 并不是采用 Velocity Tools 那种基于 Servlet 的方式来调用 velocity 模板,所以我们没法只是简单的替换成 VelocityLayoutServlet 就能解决。在 Struts 2 的默认配置中定义了一个名为 velocity 的 ResultType,它对应的类是 org.apache.struts2.dispatcher.VelocityResult 。该类没有跟 Velocity Tools 打交道,而是直接调用 Velocity 的核心 API 来执行 velocity 模板。因此我们只好根据 Velocity Tools 项目中 VelocityLayoutServlet 的思路来改写 Struts 2 对 velocity 模板的处理方法。

在处理之前我们先大概解释一下 VelocityLayoutServlet 的工作原理,先看几个程序源码:

<!-- layout.vm -- >
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<HTML>
  <HEAD>
    <TITLE>${page_title}</TITLE>
  </HEAD>
  <BODY>
	${screen_content}
  </BODY>
</HTML>

上面是布局页面的代码,这个布局页面包含两个变量,一个是 $page_title,用来指定页面标题;第二个是 $screen_content,这是 VelocityLayoutServlet 定义的变量,这个变量最终会被替换成实际页面的内容。

## index.vm
#set($layout = “layout.vm”) ##选择布局页面
#set($page_title =”Struts2Velocity”)
#set($name = “Winter Lau”)
<p>
Hello <b>$name</b>, Welcome to Struts2 with velocity <br/>
</p>

上面是另外一个页面 index.vm 的代码。当 VelocityLayoutServlet 处理 index.vm 时,首先执行 index.vm,并把执行后的结果保存在一个字符串中,并往 VelocityContext 中增加变量名为 $screen_content,值为 index.vm 的执行结果,然后执行 layout.vm 页面,这样最终呈现在用户面前的就是 index.vm 嵌入在 layout.vm 的效果。

因此我们可以依照上面的原理,从 VelocityResult 中继承一个子类并重写其 doExecute 这个方法。详细的代码请看附件中的源码。

类编写完毕同样需要对 Struts 进行如下配置(struts.xml),这个配置是告诉 Struts 使用 struts2.VelocityLayoutResult 这个类来处理采用 velocity 开发的视图:

<result-types>
<result-type name="velocity" class="struts2.VelocityLayoutResult"/>
</result-types>

接下来就可以测试新的功能了,我们配置 action 如下,打开浏览器访问 http://localhost:<port>/index.do 看看效果吧。

<action name="index">
    <result type="velocity">/index.vm</result>
</action>

这样我们不需要 VelocityTools 也可以让 Struts 2 支持 Velocity Layout 了。





回页首


第三个问题:处理 velocity 模板时存在编码问题

Velocity 有几个参数跟编码有关的分别是:

default.contentType=text/html
input.encoding=GBK
output.encoding=UTF-8

其中第一个参数指定了 velocity 模板执行后输出到浏览器时的 ContentType;第二个参数是 Velocity 引擎以什么编码方式读取 velocity 模板文件;第三个参数是输出页面的编码。

但是采用 Struts 2 后发现这三个参数并没有起作用,深入 Struts 2 的代码发现在处理 vm 编码时采用的是 Struts 2 的编码配置,而且读取 velocity 模板文件和输出 html 到浏览器采用的相同的编码,这种方式个人觉得 Struts 2 处理欠妥当,为了保留 Velocity 的特色,在 VelocityLayoutResult 的基础上进一步改进。我们需要重载 VelocityResult 的 setVelocityManager 方法,因为这个方法是用来初始化 Velocity 引擎的,我们在初始化的过程中读取前面提到的三个配置参数并把参数值保存在类的属性中,详细的代码如下:

@Inject
@Override
public void setVelocityManager(VelocityManager mgr) {
	super.setVelocityManager(mgr);
	if(mgr != null && velocityManager==null){
		this.velocityManager = mgr;
        ServletContext ctx = ServletActionContext.getServletContext();
        velocityManager.init(ctx);
VelocityEngine engine = velocityManager.getVelocityEngine();
		defaultLayout = (String)engine.getProperty(PROPERTY_DEFAULT_LAYOUT);
		layoutDir = (String)engine.getProperty(PROPERTY_LAYOUT_DIR);
        if (!layoutDir.endsWith("/")){
            layoutDir += '/';
        }
        
        if (!layoutDir.startsWith("/")){
            layoutDir = "/" + layoutDir;
        }

        // for efficiency's sake, make defaultLayout a full path now
        defaultLayout = layoutDir + defaultLayout;
        
        inputEncoding = (String)engine.getProperty(PROPERTY_INPUT_ENCODING);
        outputEncoding = (String)engine.getProperty(PROPERTY_OUTPUT_ENCODING);
        contentType = (String)engine.getProperty(PROPERTY_CONTENT_TYPE);

        if (outputEncoding != null && contentType != null) {
            contentType = contentType + ";charset=" + outputEncoding;
        }
	}
}

同时修改 doExecute 方法以使用正确的编码进行处理,主要代码如下(请注意标识为粗体的代码):

Template t = getTemplate(stack, velocityManager.getVelocityEngine(), 
	invocation, finalLocation, inputEncoding);

Context context = createContext(velocityManager, stack, request, response, finalLocation);
//将页面执行结果写入到字符串缓存中
StringWriter sw = new StringWriter();
t.merge(context, sw);            
context.put(KEY_SCREEN_CONTENT, sw.toString());

Object obj = context.get(KEY_LAYOUT);
String layout = (obj == null) ? null : obj.toString();
if (layout == null)
{
    // no alternate, use default
    layout = defaultLayout;
}
else
{
    // make it a full(er) path
    layout = layoutDir + layout;
}

Template layout_vm = null;
try
{
    //System.err.println("-------------- > layout="  + layout);
    //load the layout template
	layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), 
		invocation, layout, inputEncoding);
}
catch (Exception e)
{
	velocityManager.getVelocityEngine().getLog().error(
		"VelocityLayoutResult: Can't load layout \"" + layout + "\": " + e);

    // if it was an alternate layout we couldn't get...
    if (!layout.equals(defaultLayout))
    {
        // try to get the default layout
        // if this also fails, let the exception go
    	layout_vm = getTemplate(stack, velocityManager.getVelocityEngine(), 
			invocation, defaultLayout, inputEncoding);
    }
}

Writer writer = new OutputStreamWriter(response.getOutputStream(), 
	outputEncoding);
response.setContentType(contentType);
layout_vm.merge(context, writer);
writer.flush();





回页首


总结

本文是在初步使用 Struts 2 时所发现的一些问题,通过对 Struts 2 代码的研究找出相应的解决办法,如果你在使用 Struts 2 和 Velocity 中有发现其他问题,欢迎跟我联系一起探讨。



参考资料

学习
  • developerWorks Struts 框架专题:该专题包含大量和 Struts 框架相关的技术文章和教程。

  • Struts 与 Velocity 的集成(developerWorks,2005 年 10 月):《Struts Recipes》 的合著者 George Franciscus 带您一步步地把 Velocity 模板引擎集成进 Struts 应用程序。

  • Java 技术专区:寻找 Java 编程各方面的技术文章。


获得产品和技术


关于作者

刘冬,一直使用 J2EE/J2ME 从事移动互联网方面的开发,拥有开源博客产品 DLOG4J,目前 DLOG4J 已经推出3.0多用户版。您可以通过 Java 自由人网站来跟他联系,网址是:http://www.dlog.cn/javayou ,另外他的邮件地址是 javayou@gmail.com

分享到:
评论

相关推荐

    struts2与velocity集成 实例

    Struts2和Velocity是两种广泛应用于Java Web开发的技术。Struts2是一个强大的MVC(Model-View...通过实践这个实例,你可以深入学习到如何在Struts2中使用Velocity模板来构建动态网页,进一步提升你的Java Web开发技能。

    struts2整合velocity

    在Struts2中整合Velocity还需要配置Velocity相关的依赖,确保项目中包含Velocity Engine库,并在Struts2的配置中指定Velocity工具库: ```xml &lt;constant name="struts.action.extension" value=".do"/&gt; ...

    struts2+velocity

    9. **国际化与本地化**:Struts2和Velocity都支持多语言环境。通过资源配置,可以轻松实现页面内容的国际化。 通过这个"Struts2VsVelocity"项目,开发者不仅可以学习到如何在实际项目中整合这两个框架,还能深入...

    Struts2 整合 velocity最简单工程 最少的jar包

    - Struts2需要velocity-plugin.jar来支持Velocity结果类型。在`struts.properties`或`struts-plugin.xml`中启用Velocity插件: ```xml &lt;constant name="struts.velocity.properties" value="velocity.properties...

    struts2+Velocity替换jsp项目源码

    通过这个"struts2+Velocity替换jsp项目源码",你可以学习到如何将一个传统的基于JSP的Web应用转换为使用Velocity作为视图层,从而提升项目的可维护性和性能。这是一个很好的实践案例,对于深入理解和掌握Struts2与...

    Struts2+velocity 整合jar包

    Struts2+velocity 整合时所用的jar包 资源目录如下 commons-collections-3.1 commons-digester-2.0 commons-fileupload-1.2.2 commons-lang-2.5 freemarker-2.3.16 ognl-3.0.1 oro-2.0.8 struts2-core-2.2.3.1 ...

    velocity+struts2实例,适合开发和整合使用

    在实际开发中,Struts2和Velocity的组合可以大大提高开发效率。你可以创建可重用的Action和模板,例如,通过继承ActionSupport基类,实现通用的错误处理和消息展示。同时,Velocity模板可以通过宏定义和模板继承来...

    Struts2+velocity jar

    Struts2是一个MVC(Model-View-Controller)框架,它为构建基于Java应用的Web用户界面提供了强大的支持,而Velocity则是一个模板引擎,主要用于生成动态内容,使得开发者可以专注于业务逻辑,而不必关心HTML或其他...

    struts2+velocity jar包

    Struts2和Velocity是两个非常重要的Java开源框架,它们在Web开发中有着广泛的应用。Struts2是一个基于MVC(Model-View-Controller)设计模式的框架,它为构建Java Web应用程序提供了强大的支持。而Velocity则是一个...

    mongo集成spring struts2 json velocity

    MongoDB提供了高效的数据库支持,Spring框架带来了强大的企业级特性,Struts2实现了MVC设计模式,JSON简化了数据交换,而Velocity则优化了视图层的渲染。理解并掌握这些技术的集成,对于提升Web开发能力具有重要的...

    struts2整合velocity含源码

    Struts2和Velocity是两种在Java Web开发中广泛使用的开源框架。Struts2是一个MVC(Model-View-Controller)框架,它极大地简化了Java Web应用程序的开发,而Velocity则是一个快速、简单、可嵌入的Java模板引擎,用于...

    maven构建spring struts2 ibatis velocity小实例

    在Spring基础上,Struts2可以进一步简化控制器层的开发,提供丰富的拦截器机制和强大的Action支持。通过Struts2的配置,我们可以定义Action类及其映射,处理用户的请求,并将结果转发给相应的视图。 iBatis是轻量级...

    struts2 velocity

    Struts2是一个流行的Java web应用程序框架,它提供了一种组织和控制MVC(模型-视图-控制器)架构的方式,而Velocity则是Apache软件基金会开发的一个模板引擎,用于生成动态内容。Struts2与Velocity结合使用,可以...

    Struts2与Velocity模板

    Struts2 与 Velocity 模板 Velocity 是一种基于 JAVA 的模板引擎,开发人员使用简单的模板语言就可以快速开发显示层,它使得显示层与程序代码分离。在 Struts2 框架中,Velocity 模板引擎可以与 Struts2 集成,实现...

    使用了Struts结构和Velocity模板技术的BLOG

    3. **宏库**:Velocity支持宏,类似于函数,可以复用代码,提高开发效率。 在“使用了Struts结构和Velocity模板技术的BLOG”项目中,每个博客文章可能对应一个Struts Action,处理用户的操作,如查看、编辑、评论等...

    struts2+spring+velocity扩展实例V1版本

    这个实例是一个学习和实践Struts2、Spring和Velocity集成的好材料,对于想要深入了解这三大框架的开发者来说,通过分析源代码,可以学习到如何有效地组合使用这些工具,提升自己的Java Web开发技能。后续版本的整合...

    Struts2&&Velocity

    学习和理解Struts2与Velocity的整合,可以帮助开发者更好地掌握Java Web应用的开发流程,提升项目的可维护性和开发效率。通过熟练运用这两者,开发者可以构建出清晰分离的业务逻辑、控制逻辑和视图,从而实现更高效...

    Struts2 与 Velocity 实例

    Struts2 和 Velocity 是两种广泛应用于Java Web 开发中的开源框架。Struts2 主要负责MVC(模型-视图-控制器)架构的实现,提供了一种组织应用程序逻辑的方式,而Velocity 则是一个模板引擎,专注于视图层的渲染,...

    struts2Velocity.zip_velocity

    1. **配置集成**:在Struts2的配置文件(struts.xml)中,需要声明Velocity结果类型,以便Struts2知道如何处理 Velocity模板。通过添加`&lt;result-type&gt;`标签并指定`class`属性为`org.apache.struts2.views.velocity....

    struts2整合velocity的6个jar包

    commons-fileupload-1.2.1.jar commons-io-1.3.2.jar commons-logging-1.0.4.jar freemarker-2.3.13.jar struts2-core-2.1.6.jar xwork-2.1.2.jar

Global site tag (gtag.js) - Google Analytics