不少人看過或了解過Velocity,名稱字面翻譯為:速度、速率、迅速,用在Web開發里,用過的人可能不多,大都基本知道和在使用Struts,到底Velocity和Struts是如何聯系,怎么看待Velocity呢?讓我們來嘗試一下,了解Velocity的概念,通過在這里的介紹,強調在技術選擇上的問題,讓大家在選擇項目開發時,可以考慮Velocity,另外也讓大家了解它的思想,畢竟它提供了一個很好的思維方式,給大家換換筋骨,換一種思考的方式。
本文基于你對Java開發有一定基礎,知道MVC,Struts等開發模式。
Velocity是一種Java模版引擎技術,該項目由Apache提出,由另外一種引擎技術Webmacro引深而來。那什么是官方的Velocity定義呢?Apache對它的定義是:一種基于Java的模板引擎,但允許任何人使用簡單而強大的模板語言來引用定義在Java代碼中的對象。目前最新的版本是1.4,可以在http://jakarta.apache.org/velocity/index.html查找更多信息。
?其實說白了Velocity也就是MVC架構的一種實現,但它更多的是關注在Model和View之間,作為它們的橋梁。對于MVC的最流行架構Struts來說,相信大家都不陌生,很多開發人員已經大量在使用Struts架構,包括IBM的Websphere 5以上的管理平臺版本,Struts技術很好的實踐了MVC,它有效的減少Java代碼在View(Jsp)中的出現,但在Model和View之間還是依靠Struts的Taglib技術來實現,試想如果前臺開發的網頁設計師對Struts乃至Taglib不熟(相信也挺難熟的,包括后期的維護人員也一樣),將會對網頁設計師和前臺開發工程師的相互協作開發帶來很大的難度,現實開發中也還是存在這樣事實,網頁設計師和前臺開發之間的工作或多或少還是存在一定的耦合,怎樣最大限度的解決這個難題呢?還是讓我們來看看Velocity或者說這個概念吧。
?先做一個最簡單的Velocity開發例子,讓大家看看Velocity是怎樣工作的:
1、?創建1個文件,文件名為:hellovelocity.vm,即velocity模版(其實和html一樣),內容:
Welcome $name to Javayou.com!
today is $date.
2、?創建1個java文件,HelloVelocity.java,內容:
package com.javayou.velocity;
import java.io.StringWriter;
import java.util.*;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
/**
?* @author Liang.xf 2004-12-14
?*/
public class HelloVelocity {
?public static void main(String[] args) throws Exception {
??
??//初始化并取得Velocity引擎
??VelocityEngine ve = new VelocityEngine();
??ve.init();?
??//取得velocity的模版
??Template t = ve.getTemplate("hellovelocity.vm");
??//取得velocity的上下文context
??VelocityContext context = new VelocityContext();
??//把數據填入上下文
??context.put("name", "Liang");
??context.put("date", (new Date()).toString());
??
??//為后面的展示,提前輸入List數值
??List temp = new ArrayList();
??temp.add("1");
??temp.add("2");
??context.put("list", temp);
??//輸出流
??StringWriter writer = new StringWriter();
??//轉換輸出
??t.merge(context, writer);
??System.out.println(writer.toString());
?}
}
3、?在http://jakarta.apache.org/site/binindex.cgi上下載Velocity 1.4 zip,解壓后獲取velocity-1.4.jar,用它來編譯上面的類HelloVelocity.java。
4、?把1上的hellovelocity.vm copy到運行的當前目錄下,運行HelloVelocity還需要其他類包,可以從下載后的velocity1.4.zip來,\velocity-1.4\build\lib,把commons-collections.jar、logkit-1.0.1.jar引入后運行java -cp .\bin; -Djava.ext.dirs=.\lib2 com.javayou.velocity.HelloVelocity,假設class編譯到.\bin目錄,而我們所需的類包放到.\lib2目錄內,運行結構如下:
Welcome Liang to Javayou.com!
today is Tue Dec 14 19:26:37 CST 2004.
以上是最簡單的運行結果,怎么樣,知道個大概吧,模版hellovelocity.vm里的2個定義變量$name和$date分別被context.put("name", "Liang")和context.put("date", (new Date()).toString())所設的值替代了。
由此看來業務流程處理包括業務結果基本在model這層全部解決,而view這一層基本只用使用簡單的VTL(Velocity Template Language)來展示。這樣,Jsp豈不是不用了么?是的,這樣的使用模式有點象早前的CGI方式:)由Velocity自動輸出代碼,并且Velocity在這方面的能力也很強,Turbine里就采用了Velocity來產生很多代碼。
在Velocity中,變量的定義都是使用“$”開頭的,$作為Velocity的標識符。字母、數字、中劃和下劃線都可以作為Velocity的定義變量。
此外我們還需要注意的是Velocity特色的變量定義,如:$student.No、$student.Address,它有2層含義:第1種是如果student是hashtable,則將從hashtable中提取key為No和Address的值,另外第2種就是它有可能是調用方法,即上面2個變量將被轉換為student.getNo()和student.getAddress()。Velocity對在servlet中的java code返回的值有對象,還可以調用對象的方法,如$ student.getAddress()等等,在此就不一一舉例和深入了。
上面的例子只是簡單的舉例,現在當然不少人已經不滿足這樣的例子了,實際的應用中我們還常常需要作些選擇性展示和列舉一些迭代數據,如List列表,當然Velocity(具體來說應該是VTL模版語言)也支持這項功能,此外還支持其他一些常用的展示,如模版內部的變量(如Jsp內的變量),還有強大一些的如創建宏以實現自動化,讓我們繼續接著往下看吧。
我們還是使用上面的例子,把模版hellovelocity.vm中的內容改為:
#set( $iAmVariable = "good!" )
Welcome $name to Javayou.com!
today is $date.
$iAmVariable
重新執行上面的運行命令,結果:
Welcome Liang to Javayou.com!
today is Tue Dec 14 22:44:39 CST 2004.
good!
可以看得模版中的變量定義為# set開頭的語句,不是很難理解,執行后模版中的變量$iAmVariable都轉換成定義的值:good!
再來看看簡單的選擇,把模版hellovelocity.vm中的內容改為:
#set ($admin = "admin")
#set ($user = "user")
#if ($admin = = $user)
? Welcome admin!
#else
? Welcome user!
#end
執行運行命令,結果:
? Welcome user!
可以看到判斷語句只是簡單的#if ()、#else、#end,不是很復雜。
接著繼續來看看迭代數據吧,把模版hellovelocity.vm中的內容改為:
#foreach( $product in $list )
?
$product
#end
?
執行運行命令,結果:
1
2
把在例子中預先保存在VelocityContext的List中的值列舉了出來,是不是很方便啊?僅僅只是用了#foreach($variable in xx) 而已,如果上面的List換成Hashtable,則可以用下面的語法:
#foreach($key in $hashVariable.keySet() )
?
$key ‘s value: $ hashVariable.get($key)
#end
一點不覺得這些腳本很復雜。
還有不少人還會問,如果是javabean怎么辦?好的,我們增加一個bean:
package com.javayou.velocity;
?
/**
?* @author Liang.xf? 2004-12-14
?*/
public class Student {
?//注意class的屬性是public的
?public String no = "";
?public String address = "";
?public Student(String _no, String _address) {
??no = _no;
??address = _address;
?}
?
?public String getAddress() {
??return address;
?}
?
?public void setAddress(String address) {
??this.address = address;
?}
?
?public String getNo() {
??return no;
?}
?
?public void setNo(String no) {
??this.no = no;
?}
}
這個Student是實足的javabean,或者說是data bean,常見的用來裝載數據的類,然后我們修改HelloVelocity.java,把:
temp.add("1");
?temp.add("2");
替換成:
?temp.add(new Student("123", "Guangzhou"));
?temp.add(new Student("456", "Zhuhai"));
再把hellovelocity.vm的內容改為:
#foreach ($s in $students)
? <$velocityCount> Address: $s.address
#end
重新編譯和執行運行命令,結果如下:
? <1> Address: Guangzhou
? <2> Address: Zhuhai
這樣把list中Student的數據打印了出來,大功告成!這里用了Velocity的內建變量$velocityCount,指的是默認的列舉序號,從1開始,也可以改成0開始,但需要在Velocity.properties中更改,Velocity.properties位于velocity-1.4.jar包內的目錄org\apache\velocity\runtime\defaults 下。
再復雜一些的迭代怎么處理呢?我們看看下面的模版例子就清楚了:
#foreach ($element in $list)
??? -- inner foreach --
??? #foreach ($element in $list)
??????? This is $element.
??????? $velocityCount
??? #end
??? -- inner foreach --
??? -- outer foreach --
??? This is $element.
??? $velocityCount
??? -- outer foreach --
#end
看出來了吧,Velocity是支持標簽嵌套的,這個可是很強大的功能,這里就不深入演示了,如果有興趣,自己試試吧。
其實,稍為深入思考剛剛我們舉的例子,就已經可以看出來,Velocity的用處在哪里?即Servlet + Velocity的模式,另外,還記得我們早期Jsp開發的模式Jsp+JavaBean嗎?在這里,我們更改為Servlet+JavaBean+Velocity,想想,是不是已經替代了Jsp+JavaBean,并更徹底的把Java代碼去除在Jsp(vm)外,如果光使用Struts(Servlet+Jsp),那么帶來的代價是Java代碼總或多或少出現在Jsp上,即使可以做到不出現Java代碼,但做過復雜架構系統的開發者都知道,代價也是很昂貴的,并且在可維護性、和網頁設計師的集成開發上存在一定的困難,所以我們在這里能感覺到,Servlet+JavaBean+Velocity的模式較好的實現了OOD的概念。而在效率上,大家也不用擔心,此種結合方式比Servlet+Jsp的方式要高效一些。
愿意了解Velocity的人應該不少,但真正實用到項目的,也許不多(還是有些項目在使用,如Jute),畢竟和Jsp比起來,Jsp更標準、更廣泛使用和有不少開發工具已經支持Jsp開發。但Velocity的功能不會僅僅局限在和Jsp競爭的局面,由上可看出它在自動代碼輸出方面功能很強,前面提到Turbine就是采用Velocity來生成很多代碼,你也可以稍加改動就可以做成代碼生成器,或其他模版生成上,都是很不錯的想法。
好了,我們再來看看要深入Velocity來做項目,還需要注意的一些常見問題吧,首先是國際化的問題,
Velocity本身支持模版的國際化編碼轉換,看看Velocity提供的方法:
Public Template getTemplate (Stirng template, String encoding),
由此推測這樣做其實不能徹底的做到國際化。
?//------------------------------------------(2)--------------------------------------------
/*
?* Copyright 2000-2001,2006 The Apache Software Foundation.
?*
?* Licensed under the Apache License, Version 2.0 (the "License");
?* you may not use this file except in compliance with the License.
?* You may obtain a copy of the License at
?*
?*????? http://www.apache.org/licenses/LICENSE-2.0
?*
?* Unless required by applicable law or agreed to in writing, software
?* distributed under the License is distributed on an "AS IS" BASIS,
?* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
?* See the License for the specific language governing permissions and
?* limitations under the License.
?*/
Servlet直接繼承VelocityServlet。需要引入velocity-dep-1.4.jar,velocity-1.4.jar兩個包。
package com.javayou;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
/**
?* Sample of how to use the VelocityServlet.
?* This example shows how to add objects to the context and
?* pass them to the template.
?*
?* @author willians He
?* @author <a href="mailto:hzxmsn@163.com">willians He.</a>
?* @version $Id: SampleServlet.java,v 1.5.8.1 2006/07/15 10:01:29 geirm Exp $
?*/
public class SampleServlet extends VelocityServlet
{
???
??? /**
???? *?? Called by the VelocityServlet
???? *?? init().? We want to set a set of properties
???? *?? so that templates will be found in the webapp
???? *?? root.? This makes this easier to work with as
???? *?? an example, so a new user doesn't have to worry
???? *?? about config issues when first figuring things
???? *?? out
???? */
??? protected Properties loadConfiguration(ServletConfig config )
??????? throws IOException, FileNotFoundException
??? {
??????? Properties p = new Properties();
??????? /*
???????? *? first, we set the template path for the
???????? *? FileResourceLoader to the root of the
???????? *? webapp.? This probably won't work under
???????? *? in a WAR under WebLogic, but should
???????? *? under tomcat :)
???????? */
??????? String path = config.getServletContext().getRealPath("/");
??????? if (path == null)
??????? {
??????????? System.out.println(" SampleServlet.loadConfiguration() : unable to "
?????????????????????????????? + "get the current webapp root.? Using '/'. Please fix.");
??????????? path = "/";
??????? }
??????? p.setProperty( Velocity.FILE_RESOURCE_LOADER_PATH,? path );
??????? /**
???????? *? and the same for the log file
???????? */
??????? p.setProperty( "runtime.log", path + "velocity.log" );
??????? return p;
??? }
??? /**
???? *? <p>
???? *? main routine to handle a request.? Called by
???? *? VelocityServlet, your responsibility as programmer
???? *? is to simply return a valid Template
???? *? </p>
???? *
???? *? @param ctx a Velocity Context object to be filled with
???? *???????????? data.? Will be used for rendering this
???? *???????????? template
???? *? @return Template to be used for request
???? */??
??? public Template handleRequest( HttpServletRequest request,
?HttpServletResponse response, Context ctx )
??? {???????
??????? /*
???????? *? set up some data to put into the context
???????? */
??????
??????? String p1 = "Bob-->congratulation to? Bob";
??????? String p2 = "Harold-->congratulation to? Harold";
??????? String p3 = "Bob--->this is Wrong enter Bob";
??????? String p4 = "Harold-->this is Wrong enter? Harold";
??????? String jpgurl2="/test/picture/lxy01.jpg";
??????? String jpgurl1="/test/picture/ch020.jpg";
???????
//????? 為后面的展示,提前輸入List數值
??????? List temp = new ArrayList();
??????? temp.add(new Students("123", "Guangzhou"));
??????? temp.add(new Students("321", "Guangzhou"));
??????? temp.add(new Students("456", "Shanghai"));
??????? temp.add(new Students("654", "Shanghai"));
??????? ctx.put("list", temp);
???????
??????? Vector personList = new Vector();
??????? Vector personList2 = new Vector();
???????
??????? String name=null;
??????? String pass=null;
??????? if(!request.getParameterMap().isEmpty())
??????? {
??????????? name = request.getParameter("Account");
??????????? pass = request.getParameter("Password");
???????????
??????? }else {
??????????? ?name="myname";
??????????? ?pass="123456";
??????? }
???????
??????? if(name.equals("myname")&&pass.equals("123456")) {??????
??????????? ?personList.addElement( p1 );
??????????? ?personList.addElement( p2 );
??????????? ?personList2.addElement( jpgurl1 );
??????? }
??????? else{
??????????? ?personList.addElement( p3 );????
??????????? ?personList.addElement( p4 );
??????????? ?personList2.addElement( jpgurl2 );
??????? }
??????? /*
???????? *? Add the list to the context.
???????? *? This is how it's passed to the template.
???????? */
??????? ctx.put("theList", personList );
??????? ctx.put("theImageList", personList2 );
???????
??????? /*
???????? *? get the template.? There are three possible
???????? *? exceptions.? Good to know what happened.
???????? */
??????? Template outty = null;
???????
??????? try
??????? {
??????????? //outty =? getTemplate("sample.html");
??????????? outty=getTemplate("templat/picMoudle/pic1.html");
???????????
???????????
??????? }
??????? catch( ParseErrorException pee )
??????? {
??????????? System.out.println("SampleServlet : parse error for template " + pee);
??????? }
??????? catch( ResourceNotFoundException rnfe )
??????? {
??????????? System.out.println("SampleServlet : template not found " + rnfe);
??????? }
??????? catch( Exception e )
??????? {
??????????? System.out.println("Error " + e);
??????? }
??????? return outty;
??? }
??
}
?
Students.java
/*
?* 創建日期 2006-7-18
?*
?* TODO 要更改此生成的文件的模板,請轉至
?* 窗口 - 首選項 - Java - 代碼樣式 - 代碼模板
?*/
package com.javayou;
/**
?* @author?Willian He
?*
?*/
public class Students {
??? public String no = "";
??? public String address = "";
??? public Students(String _no, String _address) {
????? no = _no;
????? address = _address;
??? }
??? public String getAddress() {
??????? return address;
?????? }
??? public void setAddress(String address) {
??????? this.address = address;
?????? }
??? public String getNo() {
??????? return no;
?????? }
??????
?????? public void setNo(String no) {
??????? this.no = no;
?????? }
}
模板
pic1.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
?<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
???
?<title>Sample Picture page</title></head>
<body bgcolor="#ffffff">
<center>
<h2>Hello ,How are you!</h2>
<i>Here's the list of picture</i>
#set ($foo="The Velocity")
<table width="750" height="200" border="1" cellpadding="1" cellspacing="1">
??? <tr>
?? <td bgcolor="#eeeeee" align="center">
???Show the picture??<br>?
??##foreach ($s1 in $list)
???#foreach ($s in $list)
????#if($s.address=="Shanghai")
?????this is<$velocityCount> Address: $s.address $s.no<br>
????#else?
?????that is<$velocityCount> Address: $s.no,$s.address <br>?
????#end
???#end
??##end???
??????? </td>
??? </tr>
??? #foreach ($name in $theList)
??? <tr align="center">
??????? <td bgcolor="#eeeeee">$name</td>
??? </tr>
??? #end
??? #foreach ($name in $theImageList)
??? <tr>
??????? <td align="center"><img src="$name"? title="give me" /></td>
?</tr>
?<tr>
??<td align="center"><font color="#ff0255">$name</font></td>
??? </tr>
??? #end
? <tr>
?????? <td bgcolor="#green" align="center">$foo</td>
??? </tr>
</table>
#macro(tablerows $color $somelist)
#foreach($something in $somelist)
?<tr>
?<td bgcolor=$color>$something</td>
?</tr>
#end
#end
#set($greatlakes=["Superior","Michigan","Huron","Erie","Ontario"])
#set($color=("bllack"))
<table>
#tablerows($color $greatlakes)
</table>
<a href="http://localhost:8080/test/MyJsp.jsp">回上一頁</a>
</center>
</html>