3 实战
通过上面的演练,我们知道了如何定制自己Turbine应用中的Layout、Navigation和Screen。本节将带领大家一起从头实现。
3.1 Velocity
在上面的演练中,我们一句java代码都没编写,连Web Server都没有重启,仅仅是修改了一些“.vm”文件,就实现了页面布局的调整、显示内容的变化等工作,而这样一切神奇魔法的幕后功臣就是:Velocity。
为了在我们的Web Application中使用Velocity,需要在TurbineResources.properties文件中做如下配置:
… # 开启Velocity Service services.VelocityService.classname=org.apache.turbine.services.velocity.TurbineVelocityService … services.VelocityService.template.extension=vm services.VelocityService.default.page = VelocityPage services.VelocityService.default.screen=VelocityScreen services.VelocityService.default.layout = VelocityECSLayout services.VelocityService.default.navigation=VelocityNavigation services.VelocityService.default.error.screen = VelocityErrorScreen services.VelocityService.default.layout.template = Default.vm … services.VelocityService.file.resource.loader.path = /templates/app,/templates/flux … |
上图中只包含了几个比较重要的配置项,完整的配置模板在TDK例程的WEB-INF/conf/目录下的“TurbineResources.template”文件中。一般情况下,按照模板内的默认值进行配置就可以了,自己只需要修改以下两个选项:
l services.VelocityService.default.layout.template-默认Layout的模板名。
l services.VelocityService.file.resource.loader.path-模板文件的存放路径。以“,”间隔,根目录为Web Application所在目录。
我们先在下面的叙述中穿插讲述Velocity的使用方法,然后将另起篇幅和大家一起详细分析Velocity的执行机制。
3.2 Loader
在《Turbine简述》中提到,Turbine Servlet通过各种Loader动态装载Turbine中的五个模块:Action、Page、Screen、Navigation、Layout。Turbine中的所有Loader均派生自GenericLoader。Turbine 2.2中,GenericLoader表现为一个继承自Hashtable的纯虚类:
GenericLoader中,唯一的一个虚方法就是exec()。在这里,Turbine使用了Adapter Pattern和Command Pattern将Loader和各个Module结合在了一起,以LoginUser Action为例:
在这里,Turbine Servlet只管调用ActionLoader的exec()方法,不需要知道Action的接口信息,接口的转换在ActionLoader.exec()内完成。ActionLoader担任了Adapter Pattern中Adapter的职责,将源接口(Action.doPerform())转换成了目标接口(GenericLoader.exec())。使用Adapter Pattern,使得Turbine中各个模块的添加变得更为简单,如果日后需要的话,随时可以添加Action、Layout之外的任何模块,而对现有代码几乎没有影响。(不过值得提醒的是,Turbine目前只是定义了这个Adapter Pattern的框架,在实际的代码中并没有很好地利用)
同时,ActionLoader只管调用Action中的doPerform()方法,不需要知道LoginUser的任何信息,也不用管LoginUser是怎样接受请求,更不用管LoginUser事务处理的具体细节。Turbine通过这种方式实现了Command Pattern。这样,通过继承Action类,Turbine Application的开发者可以很轻松的扩展自己的Action,实现自己的专有事务;并且,通过这种模式,开发者很容易将已有的两个或多个Action组合为一个请求序列,以简化调用代码,使得项目代码更加清晰整洁、易于维护(当然,为了实现这一点,还需要用到Component Pattern,本文就不再针对此问题进行展开了)。
类似于ActionLoader,其他模块也按照上面的方法进行协同工作。通过使用这样的设计模式,Turbine Application就能很轻松的扩展,为再开发带来非常大的便利。再通过与TurbineResources.prooerties资源文件的结合,使得Turbine Application的可扩展性非常之强;并且,如果需要给已存在Application打补丁的话,也将是非常轻松便利。
3.3 Modules
Turbine中几大Module的具体实现都基本相同,因此,下面的篇幅将针对各模块的重点进行讲解,其他模块的相同部分将会一笔代过。
3.3.1 Screen
Screen是Turbine中最重要的表述层元素。最终页面中的大部分HTML代码将在此处生成。
3.3.1.1 HelloWorld Screen
依照惯例,我们先来看基于Velocity的HelloWorld:
package com.yourcompany.app.modules.screens;
// Velocity Stuff import org.apache.velocity.context.Context;
// Turbine Stuff import org.apache.turbine.util.RunData; import org.apache.turbine.modules.screens.VelocityScreen;
public class HelloWorld extends VelocityScreen { public void doBuildTemplate( RunData data, Context context ) throws Exception { // the context object has already been setup for you! context.put ("hello", "this is a test..."); } } |
HelloWorld Screen的包名应与TurbineResources.properties中的“module.packages”设置一致,对于此例来说,“module.packages”应做如下配置:
module.packages= com. yourcompany. app.modules |
Note:包名可以有多个,各个包名之间使用“,”进行分隔。
Okay,就像我们所看到的,我们甚至不用返回任何对象,仅需要在Velocity Context中填充一些数据就可以了!
接下来,我们需要为我们的HelloWorld Screen创建一个名为HelloWorld.vm的模板:
<p> <font color="red"> $hello of the emergency broadcast station. </font> </p> |
你没看错,就这么简单!
还记得要将它放在哪里吗?没错,就是“TEMPLATES-PATH/screens/”下。
现在,通过下面这样的URL,就能访问我们的HelloWorld了:
http://www.server.com/servlet/TurbineServlet/template/HelloWorld.vm
3.3.1.2 魔法的后面
在我们看到的最终结果中,是一个完整(fully formed)的HTML页面,.vm文件中的$hello已经被“this is a test...”所取代。
Velocity究竟在幕后做了些什么呢?你也许会感到诧异。现在,就让我来为你揭开谜底:
l 首先,Turbine会查看是否存在HelloWorld这个java类,如果存在,就执行它[i]。这个java类负责给.vm文件中的$hello赋值。
l 然后,调用Velocity的模板引擎,来解释、执行HelloWorld.vm模板文件(例如,生成相应Layout、Navigation等)
l 最后,将执行结果返回给用户
可以看到,使用Velocity后,相比起Servlet,一个页面的生成已经变得非常简单,并且,完全可以由网页设计者(负责.vm文件的编写)和程序开发员(负责java类的编写)协同完成同一个项目而又不互相牵制。而且,在这个过程中生成的模板文件是非常容易管理和重用的[ii]。此时,如果对应到MVC模式的话:
l M – java类
l V – .vm文件(完全符合HTML格式)
l C – Turbine