在Velocity 1.4版本中,org.apache.velocity.servlet.VelocityServlet继承了HttpServlet,因此它继承了HttpSetvlet的对生命周期的管理,而且,只要我们在实现自己velocity servlet的时候,继承org.apache.velocity.servlet.VelocityServlet,然后继承它的一些重要的方法就可以完成实际的业务。
下载 velocity-1.4.zip 后将它的jar包导入工程即可。
详细配置
Windows Server 2003 SP2 + SQL Server 2000 + Eclipse 3.2 + MyEclipse 5.0 + Tomcat 5.5.9 + Hibernate 3.0 + Velocity 1.4
开发测试过程
我的工程的目录结构如下所示:
VelocityServlet
│ .classpath
│ .myhibernatedata
│ .mymetadata
│ .project
│
├─.myeclipse
├─src
│ │ hibernate.cfg.xml
│ │ velocity.properties
│ │
│ └─org
│ └─shirdrn
│ │ HibernateSessionFactory.java
│ │
│ └─velocity
│ ├─dao
│ │ PersonDao.java
│ │
│ ├─entity
│ │ Person.hbm.xml
│ │ Person.java
│ │
│ ├─service
│ │ PersonService.java
│ │
│ └─servlet
│ ListPersonsServlet.java
│
└─WebRoot
├─log
│ velocity.log
│
├─META-INF
│ MANIFEST.MF
│
├─templates
│ lists.vm
│
└─WEB-INF
│ web.xml
│
├─classes
│ │ hibernate.cfg.xml
│ │ velocity.properties
│ │
│ └─org
│ └─shirdrn
│ │ HibernateSessionFactory.class
│ │
│ └─velocity
│ ├─dao
│ │ PersonDao.class
│ │
│ ├─entity
│ │ Person.class
│ │ Person.hbm.xml
│ │
│ ├─service
│ │ PersonService.class
│ │
│ └─servlet
│ ListPersonsServlet.class
│
└─lib
antlr-2.7.5H3.jar
asm-attrs.jar
asm.jar
cglib-2.1.jar
commons-collections-2.1.1.jar
commons-logging-1.0.4.jar
dom4j-1.6.jar
ehcache-1.1.jar
hibernate3.jar
jaas.jar
jaxen-1.1-beta-4.jar
jdbc2_0-stdext.jar
jta.jar
log4j-1.2.9.jar
msbase.jar
mssqlserver.jar
msutil.jar
xerces-2.6.2.jar
xml-apis.jar
下面就详细介绍上面的核心代码。
Person实体对应的POJO,这对Velocity模板中使用Person的属性非常重要,其实是Person实体的setter和getter方法,如下所示:
package org.shirdrn.velocity.entity;
/**
* Person generated by MyEclipse - Hibernate Tools
*/
public class Person implements java.io.Serializable {
// Fields
private String id;
private String name;
private String gender;
private Integer age;
private String addr;
// Constructors
/** default constructor */
public Person() {
}
/** minimal constructor */
public Person(String id, String name) {
this.id = id;
this.name = name;
}
/** full constructor */
public Person(String id, String name, String gender, Integer age, String addr) {
this.id = id;
this.name = name;
this.gender = gender;
this.age = age;
this.addr = addr;
}
// Property accessors
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return this.gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return this.age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return this.addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
编写了一个实现查询数据库表person中的数据的DAO,接口如下:
package org.shirdrn.velocity.dao;
import java.util.List;
public interface PersonDao {
public List queryPersons(String hql);
}
具体实现是在PersonService类中实现的,代码如下所示:
package org.shirdrn.velocity.service;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.shirdrn.HibernateSessionFactory;
import org.shirdrn.velocity.dao.PersonDao;
public class PersonService implements PersonDao {
public List queryPersons(String hql) {
Session session = null;
try{
session = HibernateSessionFactory.getSession();
Query query = session.createQuery(hql);
return query.list();
}
catch(HibernateException e){
e.printStackTrace();
}
finally{
HibernateSessionFactory.closeSession();
}
return null;
}
}
应用Velocity最关键,最核心的Servlet的实现是在ListPersonsServlet类中,代码如下所示:
package org.shirdrn.velocity.servlet;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.servlet.VelocityServlet;
import org.shirdrn.velocity.dao.PersonDao;
import org.shirdrn.velocity.service.PersonService;
public class ListPersonsServlet extends VelocityServlet {
protected Properties loadConfiguration(ServletConfig config)throws IOException, FileNotFoundException{ // 重写了VelocityServlet中的loadConfiguration方法
String propsFileName = config.getInitParameter(INIT_PROPS_KEY);
Properties props = new Properties();
if(propsFileName != null){
String realPath = getServletContext().getRealPath(propsFileName);
if(realPath != null){
propsFileName = realPath;
}
props.load(new FileInputStream(propsFileName)); // 加载属性配置文件
}
// 将日志文件所在路径加入到Velocity上下文中
String logFileName = props.getProperty(Velocity.RUNTIME_LOG);
if(logFileName != null){
String log = getServletContext().getRealPath(logFileName);
if(log != null){
props.setProperty(Velocity.RUNTIME_LOG,log);
}
}
// 将Velocity模板加入到Velocity上下文(Context)中
String path = props.getProperty(Velocity.FILE_RESOURCE_LOADER_PATH);
if(path != null){
String realPath = getServletContext().getRealPath(path);
if(realPath != null){
props.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, realPath);
}
}
//System.out.println("props = "+props);
//props = {file.resource.loader.path=D:/Tomcat 5.5/webapps/VelocityServlet/templates, runtime.log=D:/Tomcat 5.5/webapps/VelocityServlet/log/velocity.log}
return props;
}
public Template handleRequest(HttpServletRequest request,HttpServletResponse response,Context ctx){
PersonDao personDao = new PersonService();
List personList = personDao.queryPersons("from Person");
ctx.put("personList", personList); // 将查询的数据列表放到Velocity上下文中
Template template = null;
try {
template = getTemplate("lists.vm","UTF-8"); // 获取Velocity模板文件lists.vm
} catch (ResourceNotFoundException e) {
e.printStackTrace();
} catch (ParseErrorException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return template;
}
}
因为是VelocityServlet也是Servlet,所以要在web.xml中配置,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>ListPersonsServlet</servlet-name>
<servlet-class>org.shirdrn.velocity.servlet.ListPersonsServlet</servlet-class>
<init-param>
<param-name>org.apache.velocity.properties</param-name>
<param-value>/WEB-INF/classes/velocity.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ListPersonsServlet</servlet-name>
<url-pattern>/ListPersonsServlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
上面web.xml文件中:
<init-param>
<param-name>org.apache.velocity.properties</param-name>
<param-value>/WEB-INF/classes/velocity.properties</param-value>
</init-param>
配置了velocity.properties属性文件,该文件的内容如下所示:
default.contentType=text/html; charset=UTF-8
input.encoding=UTF-8
output.encoding=UTF-8
file.resource.loader.path = templates
runtime.log = log/velocity.log
前三行指定了输入输出的编码方式为UTF-8,这样设置以后,使用Velocity模板显示数据的时候,不会出现乱码问题。
第四行指定了模板文件所在的目录。
第五行指定了日志文件所在的目录。
我们实现的模板文件lists.vm的代码如下所示:
<html>
<head>
<title>Velocity在Servlet中的应用</title>
</head>
<body bgcolor="#FFFFFF" border="1" borderColor="green" borderDarkColor="#FFFFFF" borderLightColor="#000000">
<h1>Velocity应用的例子</h1>
<table align="center" width="60%">
<tr>
<td>ID</td>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
<td>住址</td>
</tr>
#foreach($person in $personList)
<tr>
<td>$!person.id</td>
<td>$!person.name</td>
<td>$!person.gender</td>
<td>$!person.age</td>
<td>$!person.addr</td>
</tr>
#end
</table>
</body>
</html>
上面使用$!person.addr可以使得:如果Person的某个字段为空,显示的时候用空格代替,如果使用$person.addr,则当addr字段为null的时候,页面显示的内容为$person.addr。
启动Tomcat Web Server,在浏览器地址栏中键链接:
http://localhost:8080/VelocityServlet/ListPersonsServlet
就可以看到从数据库中读取出来的Person的数据显示出来,如下所示:
Velocity应用的例子
ID | 姓名 | 性别 | 年龄 | 住址 |
200804140001 | shirdrn | male | 25 | ChangChun |
200804150001 | Poolina | female | 24 | Changchun |
200804150002 | Seliby | male | 25 | Changchun |
200804150006 | Koppa | female | 24 | Changchun |
200804150008 | Justin | male | 24 | New York |
200804150009 | Soloor | female | 24 | London |
200804150015 | Sujnn | male | 22 | Shanghai |
200804160001 | SAgoop | |||
200804160002 | SAgoop | |||
200804160003 | SAgoop | |||
200804160004 | SAgoop | |||
200804160005 | SAgoop | |||
200804160006 | SAgoop | |||
200804160007 | SAgoop | |||
200804160008 | SAgoop | |||
200804160009 | SAgoop | |||
200804160010 | SAgoop | |||
200804160011 | SAgoop | |||
200804160012 | SAgoop | |||
200804160013 | SAgoop | |||
200804160014 | SAgoop | |||
200804160015 | SAgoop | |||
200804160016 | SAgoop | |||
200804160017 | SAgoop | |||
200804160018 | SAgoop | |||
200804160019 | 异域王者 | 男 | 20 | Beijing |
200804160021 | 异域王者 | 男 | 20 | Beijing |
200804160022 | 风平浪静 | female | 22 | 长春 |
200804160023 | 风平浪静 | female | 22 | 长春 |
200804160024 | 异域王者 | 男 | 20 | Beijing |
200804170001 | 异域王者 | female | 20 | Beijing |
200804170030 | 飞鱼 | 男 | 20 | Beijing |
200804170032 | 飞鱼 | 男 | 20 | Beijing |
200804170033 | 飞鱼 | 男 | 20 | Beijing |
200804170035 | 飞鱼 | 男 | 20 | Beijing |
关于异常
如果发生下面的空指针异常:
java.lang.NullPointerException
at org.apache.velocity.runtime.resource.ResourceManagerImpl.getResource(ResourceManagerImpl.java:288)
at org.apache.velocity.runtime.RuntimeInstance.getTemplate(RuntimeInstance.java:1102)
at org.apache.velocity.runtime.RuntimeSingleton.getTemplate(RuntimeSingleton.java:324)
at org.apache.velocity.servlet.VelocityServlet.getTemplate(VelocityServlet.java:603)
at org.shirdrn.velocity.servlet.ListPersonsServlet.handleRequest(ListPersonsServlet.java:61)
at org.apache.velocity.servlet.VelocityServlet.doRequest(VelocityServlet.java:365)
at org.apache.velocity.servlet.VelocityServlet.doGet(VelocityServlet.java:318)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:856)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:744)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Thread.java:595)
可能是由于使用Velocity的版本的问题,我在开始测试的时候,使用Velocity 1.5的jar包就发生上面的异常。而且在编写ListPersonsServlet文件的时候,总是提示VelocityServlet类已经过时,其实在Velocity 1.5的JavaDoc中可以看到只有是VelocityServlet类中的属性或者方法,都是过时的,所以版本问题要处理好。
心得总结
关于Velocty中出现中文乱码问题,可以在velocity.properties文件中加入下面的三行就可以解决:
default.contentType=text/html; charset=UTF-8
input.encoding=UTF-8
output.encoding=UTF-8
非常关键的三行。