集成表现层
Velocity是Jakarta项目开发的表现层技术。有关与Velocity的详细资料可以在http://jakarta.apache.org/velocity找到。这一部分介绍如何集成Velocity到Spring中。
所需的库文件
在使用Velocity之前,你需要在你的Web应用中包含两个库文件,velocity-1.x.x.jar 和commons-collections.jar 。一般它们放在WEB-INF/lib目录下,以保证J2EE服务器能够找到,同时把它们加到你的classpath中。当然假设你也已经把spring.jar放在你的WEB-INF/lib目录下!最新的Velocity和commnons collections的稳定版本由Spring框架提供,可以从/lib/velocity和/lib/jakarta-commons目录下获取。
分发器(Dispatcher Servlet)上下文
你的Spring DispatcherServlet配置文件(一般是WEB-INF/[servletname]-servlet.xml)应该包含一个视图解析器的bean定义。我们也可以再加一个bean来配置Velocity环境。我指定DispatcherServlet的名字为‘frontcontroller’,所以配置文件的名字反映了DispatcherServlet的名字
下面的示例代码显示了不同的配置文件
<!-- ===========================================================--> <!-- View resolver. Required by web framework. --> <!-- ===========================================================--> <!-- View resolvers can be configured with ResourceBundles or XML files. If you need different view resolving based on Locale, you have to use the resource bundle resolver, otherwise it makes no difference. I simply prefer to keep the Spring configs and contexts in XML. See Spring documentation for more info. --> <bean id="viewResolver" class="org.springframework.web.servlet.view.XmlViewResolver"> <property name="cache"><value>true</value></property> <property name="location"><value>/WEB-INF/frontcontroller-views.xml</value></property> </bean> <!-- ===========================================================--> <!-- Velocity configurer. --> <!-- ===========================================================--> <!-- The next bean sets up the Velocity environment for us based on a properties file, the location of which is supplied here and set on the bean in the normal way. My example shows that the bean will expect to find our velocity.properties file in the root of the WEB-INF folder. In fact, since this is the default location, it's not necessary for me to supply it here. Another possibility would be to specify all of the velocity properties here in a property set called "velocityProperties" and dispense with the actual velocity.properties file altogether. --> <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer" singleton="true"> <property name="configLocation"><value>/WEB-INF/velocity.properties</value></property> </bean>Velocity.properties
大部分属性值和Velocity模版的位置有关。Velocity模版可以通过classpath或文件系统载入,两种方式都有各自的优缺点。从classpath载入具有很好的移植性,可以在所有的目标服务器上工作,但你会发现在这种方式中,模版文件会把你的java包结构弄乱(除非你为模版建立独立树结构)。从classpath载入的另一个重要缺点是在开发过程中,在源文件目录中的任何改动常常会引起WEB-INF/classes下资源文件的刷新,这将导致开发服务器重启你的应用(代码的即时部署)。这可能是令人无法忍受的。一旦完成大部分的开发工作,你可以把模版文件存在在jar中,并把它放在WEB-INF/lib目录下中。
velocity.properties示例
这个例子将Velocity模版存放在文件系统的WEB-INF下,客户浏览器是无法直接访问到它们的,这样也不会因为你开发过程中修改它们而引起Web应用重启。它的缺点是目标服务器可能不能正确解析指向这些文件的路径,尤其是当目标服务器没有把WAR模块展开在文件系统中。Tomcat 4.1.x/5.x,WebSphere 4.x和WebSphere 5.x支持通过文件系统载入模版。但是你在其他类型的服务器上可能会有所不同。
# # velocity.properties - example configuration # # uncomment the next two lines to load templates from the # classpath (i.e. WEB-INF/classes) #resource.loader=class #class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader # comment the next two lines to stop loading templates from the # file system resource.loader=file file.resource.loader.class=org.apache.velocity.runtime.resource.loader.FileResourceLoader # additional config for file system loader only.. tell Velocity where the root # directory is for template loading. You can define multiple root directories # if you wish, I just use the one here. See the text below for a note about # the ${webapp.root} file.resource.loader.path=${webapp.root}/WEB-INF/velocity # caching should be 'true' in production systems, 'false' is a development # setting only. Change to 'class.resource.loader.cache=false' for classpath # loading file.resource.loader.cache=false # override default logging to direct velocity messages # to our application log for example. Assumes you have # defined a log4j.properties file runtime.log.logsystem.log4j.category=com.mycompany.myapplication
Web应用的根目录标记
上面在配置文件资源载入时,使用一个标记${webapp.root}来代表Web应用在文件系统中的根目录。这个标记在作为属性提供给Velocity之前,会被Spring的代码解释成和操作系统有关的实际路径。这种文件资源的载入方式在一些服务器中是不可移植的。如果你认为可移植性很重要,可以给VelocityConfigurer定义不同的“appRootMarker”,来修改根目录标记本身的名字。Spring的文档对此有详细表述。
另一种可选的属性规范
作为选择,你可以用下面的内嵌属性来代替Velocity配置bean中的“configLocation”属性,从而直接指定Velocity属性。
<property name="velocityProperties"> <props> <prop key="resource.loader">file</prop> <prop key="file.resource.loader.class">org.apache.velocity.runtime.resource.loader.FileResourceLoader</prop> <prop key="file.resource.loader.path">${webapp.root}/WEB-INF/velocity</prop> <prop key="file.resource.loader.cache">false</prop> </props> </property>
缺省配置(文件资源载入)
注意从Spring 1.0-m4起,你可以不使用属性文件或内嵌属性来定义模版文件的载入,你可以把下面的属性放在Velocity配置bean中。
<property name="resourceLoaderPath"><value>/WEB-INF/velocity/</value></property>
视图配置
配置的最后一步是定义一些视图,这些视图和Velocity模版一起被显示。视图被定义在Spring上下文文件中。正如前面提到的,这个例子使用XML文件定义视图bean,但是也可以使用属性文件(ResourceBundle)来定义。视图定义文件的名字被定义在
WEB-INF/frontcontroller-servlet.xml文件的ViewResolver的bean中。 <!-- Views can be hierarchical, here's an example of a parent view that simply defines the class to use and sets a default template which will normally be overridden in child definitions. --> <bean id="parentVelocityView" class="org.springframework.web.servlet.view.velocity.VelocityView"> <property name="url"><value>mainTemplate.vm</value></property> </bean> <!-- - The main view for the home page. Since we don't set a template name, the value from the parent is used. --> <bean id="welcomeView" parent="parentVelocityView"> <property name="attributes"> <props> <prop key="title">My Velocity Home Page</prop> </props> </property> </bean> <!-- - Another view - this one defines a different velocity template. --> <bean id="secondaryView" parent="parentVelocityView"> <property name="url"><value>secondaryTemplate.vm</value></property> <property name="attributes"> <props> <prop key="title">My Velocity Secondary Page</prop> </props> </property> </bean>
13.4.5. 创建Velocity模版
最后,你需要做的就是创建Velocity模版。我们定义的视图引用了两个模版,mainTemplate.vm和secondaryTemplate.vm。属性文件velocity.proeprties定义这两个文件被放在WEB-INF/velocity/下。如果你在velocity.properties中选择通过classpath载入,它们应该被放在缺省包的目录下,(WEB-INF/classes),或者WEB-INF/lib下的jar文件中。下面就是我们的‘secondaryView’看上去的样子(简化了的HTML文件)。
## $title is set in the view definition file for this view. <html> <head><title>$title</title></head> <body> <h1>This is $title!!</h1> ## model objects are set in the controller and referenced ## via bean properties o method names. See the Velocity ## docs for info Model Value: $model.value Model Method Return Value: $model.getReturnVal() </body> </html>
现在,当你的控制器返回一个ModelAndView包含“secondaryView”时,Velocity就会工作,将上面的页面转化为普通的HTML页面。
表单处理
Spring提供一个标签库给JSP页面使用,其中包含了<spring:bind>标签。这个标签主要使表单能够显示在web层或业务层中的Validator验证时产生的出错消息。这种行为可以被Velocity宏和其他的Spring功能模拟实现。
验证错误
通过表单验证而产生的出错消息可以从属性文件中读取,这有助于维护和国际化它们。Spring以它自己的方式处理这些,关于它的工作方式,你可以参考MVC指南或javadoc中的相关内容。为了访问这些出错消息,需要把RequestContext对象暴露给VelocityContext中的Velocity模版。修改你在views.properties或views.xml中的模版定义,给一个名字到它的attributes里(有了名字就可以被访问到)。
<bean id="welcomeView" parent="parentVelocityView"> <property name="requestContextAttribute"><value>rc</value></property> <property name="attributes"> <props> <prop key="title">My Velocity Home Page</prop> </props> </property> </bean>
在我们前面例子的基础上,上面的例子将RequestContext属性命名为rc。这样从这个视图继承的所有Velocity视图都可以访问$rc。
Velocity的宏
接下来,需要定义一个Velocity宏。既然宏可以在几个Velocity模版(HTML表单)中重用,那么完全可以把宏定义在一个宏文件中。创建宏的详细信息,参考Velocity文档。
下面的代码应该放在你的Velocity模版根目录的VM_global_library.vm文件中。
#* * showerror * * display an error for the field name supplied if one exists * in the supplied errors object. * * param $errors the object obtained from RequestContext.getErrors( "formBeanName" ) * param $field the field name you want to display errors for (if any) * *# #macro( showerror $errors $field ) #if( $errors ) #if( $errors.getFieldErrors( $field )) #foreach($err in $errors.getFieldErrors( $field )) <span class="fieldErrorText">$rc.getMessage($err)</span><br /> #end #end #end #end
将出错消息和HTML的域关联起来
最后,在你的HTML表单中,你可以使用和类似下面的代码为每个输入域显示所绑定的出错消息。
## set the following variable once somewhere at the top of ## the velocity template #set ($errors=$rc.getErrors("commandBean")) <html> ... <form ...> <input name="query" value="$!commandBean.query"><br> #showerror($errors "query") </form> ... </html>
总结
总之,下面是上面那个例子的文件目录结构。只有一部分被显示,其他一些必要的目录没有显示出来。文件定位出错很可能是Velocity视图不能工作的主要原因,其次在视图中定义了错误的属性也是很常见的原因。
ProjectRoot | +- WebContent | +- WEB-INF | +- lib | | | +- velocity-1.3.1.jar | +- spring.jar | +- velocity | | | +- VM_global_library.vm | +- mainTemplate.vm | +- secondaryTemplate.vm | +- frontcontroller-servlet.xml +- frontcontroller-views.xml +- velocity.properties