Velocity介绍
Velocity介绍- -
1.Velocity 的使用
Velocity是一个开放源吗的模版引擎,由apache.org小组负责开发,现在新的版本是Velocity1.3.1,http://jakarta.apache.org/velocity/index.html 可以了解Velocity的新信息。
Velocity允许我们在模版中设定变量,然后在运行时,动态的将数据插入到模版中,替换这些变量。
例如:
<html>
<body>HELLO $CUSTOMERNAME</body>
</html>
我们可以在运行时得到客户的名字,然后把它插入到这个模版中替换变量$CUSTOMERNAME,整个替换过程是由Velocity进行控制的,而且java的调用代码也非常简单,如我们可以在java代码中这样调用
/***********************************************************/
//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目录
Velocity.init("D://Template//resource//jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template = Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
//把模版变量的值设置到context中
context.put("CUSTOMERNAME", "My First Template Engine ---- Velocity.");
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
/***********************************************************/
这就是整个java的代码,非常的简单。如果我们有多个模版变量,我们仅需要把这些模版变量的值设置到context中。
下面我们简单的分析一下,Velocity引擎读取模板文件时,它直接输出文件中所有的文本,但以$字符开头的除外,$符号标识着一个模版变量位置,
context.put("CUSTOMERNAME", "My First Template Engine ---- Velocity.");
当 Velocity 模板引擎解析并输出模板的结果时,模板中所有出现$CUSTOMERNAME的地方都将插入客户的名字,即被加入到VelocityContext的对象的toString()方法返回值将替代Velocity变量(模板中以$开头的变量)。
模板引擎中强大、使用频繁的功能之一是它通过内建的映像(Reflection)引擎查找对象信息的能力。这个映像引擎允许用一种方便的Java“.”类似的操作符,提取任意加入到VelocityContext的对象的任何公用方法的值,或对象的任意数据成员。
映像引擎还带来了另外一个改进:快速引用JavaBean的属性。使用JavaBean属性的时候,我们可以忽略get方法和括号。请看下面这个模板的例子。
<html>
<body>
Name:$Customer.Name()
Address:$Customer.Address()
Age:$Customer.Age()
</body>
</html>
java的代码:
/***********************************************************/
//设置客户信息
Customer mycustomer = new Customer();
mycustomer.setName("Velocity");
mycustomer.setAddress("jakarta.apache.org/velocity/index.html");
mycustomer.setAge(2);
//这个文件中设定了 Velocity 使用的 Log4j 的配置和Velocity的模版文件所在的目录Velocity.init("D://Template//resource//jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template = Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
//把模版变量的值设置到context中
context.put("Customer", mycustomer);
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
输出结果:
<html>
<body>
Name:Velocity
Address:jakarta.apache.org/velocity/index.html
Age:2
</body>
</html>
除了替换变量之外,象Velocity引擎还能做其他许多事情,它们有用来比较和迭代的内建指令,通过这些指令我们可以完成程序语言中的条件判断语句和循环语句等。
例如,我们想要输出年龄等于2的所有客户的信息,我们可以这样定义我们的模版
模版:
<html>
<body>
<table>
<tr>
<td>名称</td>
<td>地址</td>
<td>年龄</td>
</tr>
#foreach ($Customer in $allCustomer)
#if($Customer.Age()=="2")
<tr>
<td>$Customer.Name()</td>
<td>$Customer.Address()</td>
<td>$Customer.Age()</td>
</tr>
#end
#end
</table>
</body>
</html>
java的代码:
/******************************************************/
//设置客户信息
ArrayList allMyCustomer = new ArrayList();
//客户1
Customer mycustomer1 = new Customer();
mycustomer1.setName("Velocity");
mycustomer1.setAddress("jakarta.apache.org/velocity/index.html");
mycustomer1.setAge(2);
//客户2
Customer mycustomer2 = new Customer();
mycustomer2.setName("Tomcat");
mycustomer2.setAddress("jakarta.apache.org/tomcat/index.html");
mycustomer2.setAge(3);
//客户3
Customer mycustomer3 = new Customer();
mycustomer3.setName("Log4J");
mycustomer3.setAddress("jakarta.apache.org/log4j/docs/index.html");
mycustomer3.setAge(2);
//添加到allMyCustomer(ArrayList)中.
allMyCustomer.add(mycustomer1);
allMyCustomer.add(mycustomer2);
allMyCustomer.add(mycustomer3);
//这个文件中设定了Velocity使用的log4j的配置和Velocity的模版文件所在的目
Velocity.init("D://Template//resource//jt.properties");
//模版文件名,模版文件所在的路径在上一条语句中已经设置了
Template template =Velocity.getTemplate("hello.vm", "gb2312");
//实例化一个Context
VelocityContext context = new VelocityContext();
/** 注意这里我们仅仅需要给一个模版变量负值 */
context.put("allCustomer", allMyCustomer);
//开始模版的替换
template.merge(context, writer);
//写到文件中
PrintWriter filewriter = new PrintWriter(new FileOutputStream(outpath),true);
filewriter.println(writer.toString());
filewriter.close();
/******************************************************/
结果:
<html>
<body>
<table>
<tr>
<td>名称</td>
<td>地址</td>
<td>年龄</td>
</tr>
<tr>
<td>Velocity</td>
<td>jakarta.apache.org/velocity/index.html</td>
<td>2</td>
</tr>
<tr>
<td>Log4J</td>
<td>jakarta.apache.org/log4j/docs/index.html</td>
<td>2</td>
</tr>
</table>
</body>
</html>
#if 语句完成逻辑判断,这个我想不用多说了。
allCustomer对象包含零个或者多个Customer对象。由于ArrayList (List, HashMap, HashTable, Iterator, Vector等)属于Java Collections Framework的一部分,我们可以用#foreach指令迭代其内容。我们不用担心如何定型对象的类型——映像引擎会为我们完成这个任务。#foreach指令的一般格式是“#foreach in ”。#foreach指令迭代list,把list中的每个元素放入item参数,然后解析#foreach块内的内容。对于list内的每个元素,#foreach块的内容都会重复解析一次。从效果上看,它相当于告诉模板引擎说:“把list中的每一个元素依次放入item变量,每次放入一个元素,输出一次#foreach块的内容”。
2.MVC设计模型
使用模板引擎大的好处在于,它分离了代码(或程序逻辑)和表现(输出)。由于这种分离,你可以修改程序逻辑而不必担心邮件消息本身;类似地,你(或公关部门的职员)可以在不重新编译程序的情况下,重新编写客户列表。实际上,我们分离了系统的数据模式(Data Model,即提供数据的类)、控制器(Controller,即客户列表程序)以及视图(View,即模板)。这种三层体系称为Model-View-Controller模型(MVC)。
如果遵从MVC模型,代码分成三个截然不同的层,简化了软件开发过程中所有相关人员的工作。
结合模板引擎使用的数据模式可以是任何Java对象,好是使用Java Collection Framework的对象。控制器只要了解模板的环境(如VelocityContext),一般这种环境都很容易使用。
一些关系数据库的“对象-关系”映射工具能够和模板引擎很好地协同,简化JDBC操作;对于EJB,情形也类似。 模板引擎与MVC中视图这一部分的关系更为密切。模板语言的功能很丰富、强大,足以处理所有必需的视图功能,同时它往往很简单,不熟悉编程的人也可以使用它。模板语言不仅使得设计者从过于复杂的编程环境中解脱出来,而且它保护了系统,避免了有意或无意带来危险的代码。例如,模板的编写者不可能编写出导致无限循环的代码,或侵占大量内存的代码。不要轻估这些安全机制的价值;大多数模板编写者不懂得编程,从长远来看,避免他们接触复杂的编程环境相当于节省了你自己的时间。 许多模板引擎的用户相信,在采用模板引擎的方案中,控制器部分和视图部分的明确分离,再加上模板引擎固有的安全机制,使得模板引擎足以成为其他内容发布系统(比如JSP)的替代方案。因此,Java模板引擎常见的用途是替代JSP也就不足为奇了。
3.HTML处理
由于人们总是看重模板引擎用来替换JSP的作用,有时他们会忘记模板还有更广泛的用途。到目前为止,模板引擎常见的用途是处理HTML Web内容。但我还用模板引擎生成过SQL、email、XML甚至Java源代码。
Velocity 的应用示例
关键字:Java, JSP, Servlet, template, 模板, Apache, Jakarta, Velocity
读者要求:了解 Java Servlet 基本概念
Velocity 是一个基于 Java 的通用模板工具,来自于 jakarta.apache.org 。
Velocity 的介绍请参考 Velocity -- Java Web 开发新技术。这里是它的一个应用示例。
这个例子参照了 PHP-Nuke 的结构, 即所有 HTTP 请求都以 http://www.some.com/xxx/Modules?name=xxx&arg1=xxx&bbb=xxx 的形式进行处理。例子中所有文件都是 .java 和 .html , 没有其他特殊的文件格式。除了 Modules.java 是 Java Servlet, 其余的 .java 文件都是普通的 Java Class.
所有 HTTP 请求都通过 Modules.java 处理。Modules.java 通过 Velocity 加载 Modules.htm。 Modules.htm 有页头,页脚,页左导航链接,页中内容几个部分。其中页头广告、页中内容是变化部分。页头广告由 Modules.java 处理,页中内容部分由 Modules.java dispatch 到子页面类处理。
1) Modules.java
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.velocity.*;
import org.apache.velocity.context.*;
import org.apache.velocity.exception.*;
import org.apache.velocity.servlet.*;
import commontools.*;
public class Modules
extends VelocityServlet {
public Template handleRequest(HttpServletRequest request,
HttpServletResponse response,
Context context) {
//init
response.setContentType("text/html; charset=UTF-8");
response.setCharacterEncoding("utf-8");
//prepare function page
ProcessSubPage page = null;
ProcessSubPage mainPage = new HomeSubPage();
String requestFunctionName = (String) request.getParameter("name");
boolean logined = false;
String loginaccount = (String) request.getSession(true).getAttribute(
"loginaccount");
if (loginaccount != null) {
logined = true;
}
//default page is mainpage
page = mainPage;
if (requestFunctionName == null||requestFunctionName.equalsIgnoreCase("home")) {
page = mainPage;
}
//no login , can use these page
else if (requestFunctionName.equalsIgnoreCase("login")) {
page = new LoginProcessSubPage();
}
else if (requestFunctionName.equalsIgnoreCase("ChangePassword")) {
page = new ChangePasswordSubPage();
}
else if (requestFunctionName.equalsIgnoreCase("ForgetPassword")) {
page = new ForgetPassword();
}
else if (requestFunctionName.equalsIgnoreCase("about")) {
page = new AboutSubPage();
}
else if (requestFunctionName.equalsIgnoreCase("contact")) {
page = new ContactSubPage();
}
//for other page, need login first
else if (logined == false) {
page = new LoginProcessSubPage();
}
else if (requestFunctionName.equalsIgnoreCase("listProgram")) {
page = new ListTransactionProgramSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"ViewProgramItem")) {
page = new ViewTransactionProgramItemSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"UpdateProgramObjStatus")) {
page = new UpdateTransactionProgramObjStatusSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"Search")) {
page = new SearchSubPage();
}
//check if this is administrator
else if (Utilities.isAdministratorLogined(request)) {
//Utilities.debugPrintln("isAdministratorLogined : true");
if (requestFunctionName.equalsIgnoreCase("usermanagement")) {
page = new UserManagementSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"UploadFiles")) {
page = new UploadFilesSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"DownloadFile")) {
page = new DownloadFileSubPage();
}
else if (requestFunctionName.equalsIgnoreCase(
"Report")) {
page = new ReportSubPage();
}
}
else {
//no right to access.
//Utilities.debugPrintln("isAdministratorLogined : false");
page = null;
}
//Utilities.debugPrintln("page : " + page.getClass().getName());
if(page != null){
context.put("function_page",
page.getHtml(this, request, response, context));
}else{
String msg = "Sorry, this module is for administrator only.You are not administrator.";
context.put("function_page",msg);
}
context.put("page_header",getPageHeaderHTML());
context.put("page_footer",getPageFooterHTML());
Template template = null;
try {
template = getTemplate("/templates/Modules.htm"); //good
}
catch (ResourceNotFoundException rnfe) {
Utilities.debugPrintln("ResourceNotFoundException 2");
rnfe.printStackTrace();
}
catch (ParseErrorException pee) {
Utilities.debugPrintln("ParseErrorException2 " + pee.getMessage());
}
catch (Exception e) {
Utilities.debugPrintln("Exception2 " + e.getMessage());
}
return template;
}
/**
* Loads the configuration information and returns that information as a Properties, e
* which will be used to initializ the Velocity runtime.
*/
protected java.util.Properties loadConfiguration(ServletConfig config) throws
java.io.IOException, java.io.FileNotFoundException {
return Utilities.initServletEnvironment(this);
}
}
2) ProcessSubPage.java , 比较简单,只定义了一个函数接口 getHtml
import javax.servlet.http.*;
import org.apache.velocity.context.*;
import org.apache.velocity.servlet.*;
import commontools.*;
public abstract class ProcessSubPage implements java.io.Serializable {
public ProcessSubPage() {
}
public String getHtml(VelocityServlet servlet, HttpServletRequest request,
HttpServletResponse response,
Context context) {
Utilities.debugPrintln(
"you need to override this method in sub class of ProcessSubPage:"
+ this.getClass().getName());
return "Sorry, this module not finish yet.";
}
}
他的 .java 文件基本上是 ProcessSubPage 的子类和一些工具类。 ProcessSubPage 的子类基本上都是一样的流程, 用类似
context.put("page_footer",getPageFooterHTML());
的写法置换 .html 中的可变部分即可。如果没有可变部分,完全是静态网页,比如 AboutSubPage, 就更简单。
3) AboutSubPage.java
import org.apache.velocity.servlet.VelocityServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.context.Context;
public class AboutSubPage extends ProcessSubPage {
public AboutSubPage() {
}
public String getHtml(VelocityServlet servlet, HttpServletRequest request,
HttpServletResponse response, Context context) {
//prepare data
//context.put("xxx","xxxx");
Template template = null;
String fileName = "About.htm";
try {
template = servlet.getTemplate(fileName);
StringWriter sw = new StringWriter();
template.merge(context, sw);
return sw.toString();
}
catch (Exception ex) {
return "error get template " + fileName + " " + ex.getMessage();
}
}
}
其他 ProcessSubPage 的子类如上面基本类似,只不过会多了一些 context.put("xxx","xxxx") 的语句。
通过以上的例子,我们可以看到,使用 Velocity + Servlet , 所有的代码为: 1 个 java serverlet + m 个 java class + n 个 Html 文件。
这里是用了集中处理,然后分发(dispatch)的机制。不用担心用户在没有登陆的情况下访问某些页面。用户验证,页眉页脚包含都只写一次,易于编写、修改和维护。代码比较简洁,并且很容易加上自己的页面缓冲功能。可以随意将某个页面的 html 在内存中保存起来,缓存几分钟,实现页面缓冲功能。成功、出错页面也可以用同样的代码封装成函数,通过参数将 Message/Title 传入即可。
因为 Java 代码与 Html 代码完全在不同的文件中,美工与java代码人员可以很好的分工,每个人修改自己熟悉的文件,基本上不需要花时间做协调工作。而用 JSP, 美工与java代码人员共同修改维护 .jsp 文件,麻烦多多,噩梦多多。而且这里没有用 xml ,说实话,懂 xml/xls 之类的人只占懂 Java 程序员中的几分之一,人员不好找。
因为所有 java 代码人员写的都是标准 Java 程序,可以用任何 Java 编辑器,调试器,因此开发速度也会大大提高。美工写的是标准 Html 文件,没有 xml, 对于他们也很熟悉,速度也很快。并且,当需要网站改版的时候,只要美工把 html 文件重新修饰排版即可,完全不用改动一句 java 代码。
爽死了!!
4) 工具类 Utilities.java
import java.io.*;
import java.sql.*;
import java.text.*;
import java.util.*;
import java.util.Date;
import javax.naming.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.velocity.*;
import org.apache.velocity.app.*;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.*;
public class Utilities {
private static Properties m_servletConfig = null;
private Utilities() {
}
static {
initJavaMail();
}
public static void debugPrintln(Object o) {
String msg = "proj debug message at " + getNowTimeString() +
" ------------- ";
System.err.println(msg + o);
}
public static Properties initServletEnvironment(VelocityServlet v) {
// init only once
if (m_servletConfig != null) {
return m_servletConfig;
}
//debugPrintln("initServletEnvironment....");
try {
/*
* call the overridable method to allow the
* derived classes a shot at altering the configuration
* before initializing Runtime
*/
Properties p = new Properties();
ServletConfig config = v.getServletConfig();
// Set the Velocity.FILE_RESOURCE_LOADED_PATH property
// to the root directory of the context.
String path = config.getServletContext().getRealPath("/");
//debugPrintln("real path of / is : " + path);
p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path);
// Set the Velocity.RUNTIME_LOG property to be the file
// velocity.log relative to the root directory
// of the context.
p.setProperty(Velocity.RUNTIME_LOG, path +
"velocity.log");
// Return the Properties object.
//return p;
Velocity.init(p);
m_servletConfig = p;
return p;
}
catch (Exception e) {
debugPrintln(e.getMessage());
//throw new ServletException("Error initializing Velocity: " + e);
}
return null;
//this.getServletContext().getRealPath("/");
}
private static void initJavaMail() {
}
public static Connection getDatabaseConnection() {
Connection con = null;
try {
InitialContext initCtx = new InitialContext();
javax.naming.Context context = (javax.naming.Context) initCtx.
lookup("java:comp/env");
javax.sql.DataSource ds = (javax.sql.DataSource) context.lookup(
"jdbc/TestDB");
//Utilities.debugPrintln("ds = " + ds);
con = ds.getConnection();
}
catch (Exception e) {
Utilities.debugPrintln("Exception = " + e.getMessage());
return null;
}
//Utilities.debugPrintln("con = " + con);
return con;
}
public static java.sql.ResultSet excuteDbQuery(Connection con, String sql,
Object[] parameters) {
//Exception err = null;
//Utilities.debugPrintln("excuteDbQuery" + parameters[0] + " ,sql=" + sql);
try {
java.sql.PreparedStatement ps = con.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
processParameter(ps, i + 1, parameters[i]);
}
return ps.executeQuery();
}
catch (Exception e) {
//Utilities.debugPrintln(e.getMessage());
e.printStackTrace();
}
return null;
}
public static void excuteDbUpdate(String sql, Object[] parameters) {
Connection con = Utilities.getDatabaseConnection();
excuteDbUpdate(con, sql, parameters);
closeDbConnection(con);
}
public static void excuteDbUpdate(Connection con, String sql,
Object[] parameters) {
Exception err = null;
try {
java.sql.PreparedStatement ps = con.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
processParameter(ps, i + 1, parameters[i]);
}
ps.execute();
}
catch (Exception e) {
err = e;
//Utilities.debugPrintln(err.getMessage());
e.printStackTrace();
}
}
private static void processParameter(java.sql.PreparedStatement ps,
int index, Object parameter) {
try {
if (parameter instanceof String) {
ps.setString(index, (String) parameter);
}
else {
ps.setObject(index, parameter);
}
}
catch (Exception e) {
//Utilities.debugPrintln(e.getMessage());
e.printStackTrace();
}
}
public static void closeDbConnection(java.sql.Connection con) {
try {
con.close();
}
catch (Exception e) {
Utilities.debugPrintln(e.getMessage());
}
}
public static String getResultPage(
String title, String message, String jumpLink,
VelocityServlet servlet, HttpServletRequest request,
HttpServletResponse response, Context context) {
Template template = null;
context.put("MessageTitle", title);
context.put("ResultMessage", message);
context.put("JumpLink", jumpLink);
try {
template = servlet.getTemplate(
"/templates/Message.htm");
StringWriter sw = new StringWriter();
template.merge(context, sw);
return sw.toString();
}
catch (Exception ex) {
return "error get template Message.htm " + ex.getMessage();
}
}
public static String mergeTemplate(String fileName, VelocityServlet servlet,
Context context) {
Template template = null;
try {
template = servlet.getTemplate(fileName);
StringWriter sw = new StringWriter();
template.merge(context, sw);
return sw.toString();
}
catch (Exception ex) {
return "error get template " + fileName + " " + ex.getMessage();
}
}
}
velocity在典型的MVC结构实现中的突出优势在于:
1 充分体现MVC的核心思想,即:功能模块化。将HTML技术和复杂的业务逻辑拆分。velocity本身是一门语言,就像JAVA语言一样,只不过规模很小而已。但是它本身完全体现了语言的几个要素,能够充分实现由复杂的现实抽象出来的逻辑。在其他语言中的经验可以在这里得到比较完整的体会。
2 开发人员的拆分。页面的HTML部分由web开发人员来实现。业务部分由业务专家来实现。这样,项目可以并行进行,涉及人员只需保证接口正确实现即可,无其他过多的交互。可以从时间上保证项目的顺利进行。
相关文章