2. Velocity 工作原理

**Velocity 的核心类是 VelocityEngine**,它负责协调整个流程:读取模板、解析内容并结合数据模型生成结果。

简单粗暴地说,使用 Velocity 的标准流程就四步:

  1. ✅ 初始化引擎
  2. ✅ 加载模板文件
  3. ✅ 准备数据上下文
  4. ✅ 合并数据与模板输出内容

来看个实战例子

VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.init();
   
Template t = velocityEngine.getTemplate("index.vm");
    
VelocityContext context = new VelocityContext();
context.put("name", "World");
    
StringWriter writer = new StringWriter();
t.merge(context, writer);

3. Maven 依赖配置

要在项目中使用 Velocity,需要添加以下两个核心依赖:

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <version>2.0</version>
</dependency>

⚠️ 最新版本建议去 Maven 中央仓库 查找:

4. Velocity 模板语言(VTL)

Velocity Template Language (VTL) 是 Velocity 的精髓,它用最简洁的方式实现动态内容注入。

4.1 引用类型

VTL 引用以 $ 开头,分为三种类型:

  • 变量引用 - 通过 #set 定义或来自 Java 对象字段:
    #set ($message="Hello World")
    
  • 属性引用 - 访问对象字段或 getter 方法:
    $customer.name  // 等价于调用 getName()
    
  • 方法引用 - 直接调用 Java 方法:
    $customer.getName()
    

💡 所有引用最终都会被转换为字符串输出

4.2 指令系统

VTL 提供了丰富的指令集:

  • 赋值指令 - #set 用于给变量赋值:
    #set ($message = "Hello World")
    #set ($customer.name = "Brian Mcdonald")
    
  • 条件指令 - #if/#elseif/#else 实现逻辑分支:
    #if($employee.designation == "Manager")
        <h3> Manager </h3>
    #elseif($employee.designation == "Senior Developer")
        <h3> Senior Software Engineer </h3>
    #else
        <h3> Trainee </h3>
    #end
    
  • 循环指令 - #foreach 遍历集合:
    <ul>
        #foreach($product in $productList)
            <li> $product </li>
        #end
    </ul>
    
  • 文件包含 - #include 导入静态文件:
    #include("one.gif","two.txt","three.html"...)
    
  • 模板解析 - #parse 动态解析 VTL 文件:
    #parse (Template)
    
  • 动态求值 - #evaluate 运行时计算表达式:
    #set($firstName = "David")
    #set($lastName = "Johnson")
    #set($dynamicsource = "$firstName$lastName")
    #evaluate($dynamicsource)  // 输出 "DavidJohnson"
    
  • 流程控制 - #break 终止循环,#stop 终止模板渲染
  • 宏定义 - #macro 创建可复用模板片段:
    #macro(tablerows $color $productList)
        #foreach($product in $productList)
            <tr>
                <td bgcolor=$color>$product.name</td>
            </tr>
        #end
    #end
    

4.3 其他特性

  • 数学运算 - 内置基础数学函数:
    #set($percent = $number / 100)
    #set($remainder = $dividend % $divisor)
    
  • 范围操作符 - 生成数字序列:
    #set($array = [0..10])  // 创建 0-10 的数组
    
    #foreach($elem in $arr)
        $elem
    #end
    

5. Velocity Servlet 集成

Velocity 引擎本身不处理 Web 相关功能,需要结合 Servlet 使用。Velocity Tools 子项目提供了现成的 VelocityViewServlet

public class ProductServlet extends VelocityViewServlet {

    ProductService service = new ProductService();

    @Override
    public Template handleRequest(
      HttpServletRequest request, 
      HttpServletResponse response,
      Context context) throws Exception {
      
        List<Product> products = service.getProducts();
        context.put("products", products);

        return getTemplate("index.vm");
    }
}

6. 配置详解

6.1 Web 配置

web.xml 中配置 VelocityViewServlet

<web-app>
    <display-name>apache-velocity</display-name>
    <!-- 其他配置 -->
       
    <servlet>
        <servlet-name>velocity</servlet-name>
        <servlet-class>org.apache.velocity.tools.view.VelocityViewServlet</servlet-class>

        <init-param>
            <param-name>org.apache.velocity.properties</param-name>
            <param-value>/WEB-INF/velocity.properties</param-value>
        </init-param>
    </servlet>
    <!-- 其他配置 -->
</web-app>

所有 .vm 请求都交给 Velocity Servlet 处理:

<servlet-mapping>
    <servlet-name>velocityLayout</servlet-name>
    <url-pattern>*.vm</url-pattern>
</servlet-mapping>

6.2 资源加载器

Velocity 支持多种资源加载方式,可同时配置多个加载器:

  • FileResourceLoader - 文件系统加载
  • JarResourceLoader - JAR 包加载
  • ClassPathResourceLoader - 类路径加载
  • URLResourceLoader - URL 加载
  • DataSourceResourceLoader - 数据库加载
  • WebappResourceLoader - Web 应用加载

velocity.properties 中配置:

resource.loader=webapp
webapp.resource.loader.class=org.apache.velocity.tools.view.WebappResourceLoader
webapp.resource.loader.path = 
webapp.resource.loader.cache = true

7. 模板实战

Velocity 模板是视图逻辑的核心,使用 VTL 编写:

<html>
    ...
    <body>
        <center>
        ...
        <h2>$products.size() Products on Sale!</h2>
        <br/>
            We are proud to offer these fine products
            at these amazing prices.
        ...
        #set( $count = 1 )
        <table class="gridtable">
            <tr>
                <th>Serial #</th>
                <th>Product Name</th>
                <th>Price</th>
            </tr>
            #foreach( $product in $products )
            <tr>
                <td>$count)</td>
                <td>$product.getName()</td>
                <td>$product.getPrice()</td>
            </tr>
            #set( $count = $count + 1 )
            #end
        </table>
        <br/>
        </center>
    </body>
</html>

8. 页面布局管理

Velocity 提供强大的布局控制能力,通过 VelocityLayoutServlet 实现。

8.1 Web 配置

配置 VelocityLayoutServlet

<web-app>
    <!-- 其他配置 -->
    <servlet>
        <servlet-name>velocityLayout</servlet-name>
        <servlet-class>org.apache.velocity.tools.view.VelocityLayoutServlet</servlet-class>

        <init-param>
            <param-name>org.apache.velocity.properties</param-name>
            <param-value>/WEB-INF/velocity.properties</param-value>
        </init-param>
    </servlet>
    <!-- 其他配置 -->
    <servlet-mapping>
        <servlet-name>velocityLayout</servlet-name>
        <url-pattern>*.vm</url-pattern>
    </servlet-mapping>
    <!-- 其他配置 -->
</web-app>

8.2 布局模板

默认在 layout/Default.vm 查找布局文件(可配置):

tools.view.servlet.layout.directory = layout/
tools.view.servlet.layout.default.template = Default.vm

布局文件示例:

<html>
    <head>
        <title>Velocity</title>
    </head>
    <body>
        <div>
            #parse("/fragments/header.vm")
        </div>
        <div>
            <!-- 这里会插入 index.vm 的内容 -->
            $screen_content
        </div>
        <div>
            #parse("/fragments/footer.vm")
        </div>
    </body>
</html>

8.3 页面级布局覆盖

在页面开头指定特定布局:

#set($layout = "MyOtherLayout.vm")

8.4 请求参数覆盖

通过 URL 参数指定布局:

http://example.com/page.vm?layout=MyOtherLayout.vm

8.5 错误页面定制

通过 $error_cause$stack_trace 变量展示异常信息:

tools.view.servlet.error.template = Error.vm

9. 总结

本文深入探讨了 Velocity 在动态页面渲染中的强大能力,以及多种 Servlet 集成方案。

我们还有一篇专门讲解 Spring MVC 集成 Velocity 的文章,可以在 Baeldung 查看。

完整代码示例请访问 GitHub 仓库


原始标题:Introduction to Apache Velocity