Introduction to Apache Velocity – Apache Velocity简介

最后修改: 2017年 2月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Velocity is a Java-based templating engine.

Velocity是一个基于Java的模板引擎。

It’s an open source web framework designed to be used as a view component in the MVC architecture, and it provides an alternative to some existing technologies such as JSP.

它是一个开源的Web框架,被设计为MVC架构中的一个视图组件,它为一些现有的技术如JSP提供了一个替代。

Velocity can be used to generate XML files, SQL, PostScript and most other text-based formats.

Velocity可以用来生成XML文件、SQL、PostScript和大多数其他基于文本的格式。

In this article, we will explore how it can be used to create dynamic web pages.

在这篇文章中,我们将探讨如何使用它来创建动态网页。

2. How Velocity Works

2.速度是如何工作的

The core class of Velocity is the VelocityEngine.

Velocity的核心类是VelocityEngine.

It orchestrates the whole process of reading, parsing and generating content using data model and velocity template.

它利用数据模型和速度模板来协调阅读、解析和生成内容的整个过程。

Simply put, here are the steps we need to follow for any typical velocity application:

简单地说,以下是我们在任何典型的速度应用中需要遵循的步骤。

  • Initialize the velocity engine
  • Read the template
  • Put the data model in context object
  • Merge the template with context data and render the view

Let’s go through an example following these simple steps:

让我们按照这些简单的步骤来看看一个例子

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 Dependencies

3.Maven的依赖性

To work with Velocity, we need to add following dependencies to our Maven project:

为了使用Velocity,我们需要在我们的Maven项目中添加以下依赖项。

<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>

The latest version of both these dependencies can be here: velocity and velocity-tools.

这两个依赖项的最新版本可以在这里找到。velocityvelocity-tools

4. Velocity Template Language

4.Velocity模板语言

Velocity Template Language (VTL) provides the simplest and cleanest way of incorporating the dynamic content in a web page by using VTL references.

Velocity模板语言(VTL)通过使用VTL引用,提供了将动态内容纳入网页的最简单和最干净的方法。

VTL reference in velocity template starts with a $ and is used for getting the value associated with that reference. VTL provides also a set of directives which can be used for manipulating the output of the Java code. Those directives start with #.

速度模板中的VTL引用以$开头,用于获取与该引用相关的值。VTL还提供了一组指令,可用于操纵Java代码的输出。这些指令以#开头。

4.1. References

4.1.参考文献

There are three types of references in Velocity, variables, properties and methods:

在Velocity中,有三种类型的引用,变量、属性和方法。

  • variables – defined within the page using #set directive or value returned from Java object’s field:
    #set ($message="Hello World")
  • properties – refer to fields within an object; they can also refer to a getter method of the property:
    $customer.name
  • methods – refer to the method on Java object:
    $customer.getName()

The final value resulting from every reference is converted to a string when it is rendered into the final output.

每次引用产生的最终值在渲染到最终输出时都会被转换为一个字符串。

4.2. Directives

4.2.指令

VTL provides a rich set of directives:

VTL提供了一个丰富的指令集。

  • set – it can be used for setting the value of a reference; this value can be assigned to a variable or a property reference:
    #set ($message = "Hello World")
    #set ($customer.name = "Brian Mcdonald")
  • conditionals#if, #elseif and #else directives provide a way to generate the content based on conditional checks:
    #if($employee.designation == "Manager")
        <h3> Manager </h3>
    #elseif($employee.designation == "Senior Developer")
        <h3> Senior Software Engineer </h3>
    #else
        <h3> Trainee </h3>
    #end
  • loops#foreach directive allows looping over a collection of objects:
    <ul>
        #foreach($product in $productList)
            <li> $product </li>
        #end
    </ul>
  • include#include element provides the ability to import files into the template:
    #include("one.gif","two.txt","three.html"...)
  • parse#parse statement allows the template designer to import another local file which contains VTL; Velocity will then parse the content and render it:
    #parse (Template)
  • evaluate#evaluate directive can be used to evaluate VTL dynamically; this allows the template to evaluate a String at render time, for example to internationalise the template:
    #set($firstName = "David")
    #set($lastName = "Johnson")
    
    #set($dynamicsource = "$firstName$lastName")
    
    #evaluate($dynamicsource)
  • break#break directive stops any further rendering of current execution scope (i.e. #foreach, #parse)
  • stop#stop directive stops any further rendering and execution of the template.
  • velocimacros#macro directive allows the template designer to define a repeated segment of VTL:
    #macro(tablerows)
        <tr>
            <td>
            </td>
        </tr>
    #end

    This macro now can be put in any place in the template as #tablerows():

    这个宏现在可以作为#tablerows()放到模板的任何地方:

    #macro(tablerows $color $productList)
        #foreach($product in $productList)
            <tr>
                <td bgcolor=$color>$product.name</td>
            </tr>
        #end
    #end

4.3. Other Features

4.3.其他特点

  • math – a handful built-in mathematical functions, which can be used in templates:
    #set($percent = $number / 100)
    #set($remainder = $dividend % $divisor)
  • range operator – that can be used in conjunction with #set and #foreach:
    #set($array = [0..10])
    
    #foreach($elem in $arr)
        $elem
    #end

5. Velocity Servlet

5.速度型小程序

The primary job of the Velocity Engine is to generate content based on a template.

Velocity引擎的主要工作是根据一个模板生成内容。

The Engine doesn’t contain any web related functionalities in itself. To implement a web application, we need to use a servlet or servlet-based framework.

引擎本身并不包含任何与网络有关的功能。为了实现一个网络应用,我们需要使用一个Servlet或基于Servlet的框架。

Velocity provides one out of the box implementation VelocityViewServlet, which is a part of the velocity-tools subproject.

Velocity提供了一个开箱即用的实现VelocityViewServlet,它是velocity-tools子项目的一部分。

To make use of the built-in functionality provided by VelocityViewServlet, we can extend our servlet from VelocityViewServlet and override the handleRequest() method:

为了利用VelocityViewServlet提供的内置功能,我们可以从VelocityViewServlet扩展我们的Servlet,并重写handleRequest()方法。

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. Configuration

6.配置

6.1. Web Configuration

6.1.网络配置

Let’s now see how to configure the VelocityViewServlet in the web.xml.

现在让我们看看如何在web.xml中配置VelocityViewServlet

We need to specify the optional initialization parameters which include velocity.properties and toolbox.xml:

我们需要指定可选的初始化参数,包括velocity.propertiestoolbox.xml

<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>

We also need to specify the mapping for this servlet. All the requests for velocity templates (*.vm) need to be served by the velocity servlet:

我们还需要指定这个servlet的映射。所有对速度模板(*.vm)的请求都需要由velocity servlet来提供。

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

6.2. Resource Loader

6.2.资源加载器

Velocity provides flexible resource loader system. It allows one or more resource loader to be in operation at the same time:

Velocity提供了灵活的资源加载器系统。它允许一个或多个资源加载器同时运行。

  • FileResourceLoader
  • JarResourceLoader
  • ClassPathResourceLoader
  • URLResourceLoader
  • DataSourceResourceLoader
  • WebappResourceLoader

These resource loaders are configured in velocity.properties:

这些资源加载器是在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 Template

7.速度模板

Velocity template is the place where all the view generation logic is written. These pages are written using Velocity Template Language (VTL):

Velocity模板是编写所有视图生成逻辑的地方。这些页面是用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. Managing the Page Layout

8.管理页面布局

Velocity provides a simple layout control and customizable error screens for Velocity Tool based application.

Velocity为基于Velocity工具的应用程序提供简单的布局控制和可定制的错误屏幕。

VelocityLayoutServlet encapsulates this capability to render the specified layouts. VelocityLayoutServlet is an extension to VelocityViewServlet.

VelocityLayoutServlet封装了这种能力以渲染指定的布局。VelocityLayoutServletVelocityViewServlet的一个扩展。

8.1. Web Configuration

8.1.网络配置

Let’s see how to configure the VelocityLayoutServlet. The servlet is defined for intercepting the requests for velocity template pages and the layout specific properties are defined in velocity.properties file:

让我们看看如何配置VelocityLayoutServlet。该Servlet被定义为拦截对velocity模板页面的请求,布局的具体属性被定义在velocity.properties文件中。

<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 Templates

8.2.布局模板

Layout template defines the typical structure of a velocity page. By default, the VelocityLayoutServlet searches for Default.vm under the layout folder. Overriding few properties can change this location:

布局模板定义了一个速度页面的典型结构。默认情况下,VelocityLayoutServlet会在布局文件夹下搜索Default.vm。重写少数属性可以改变这个位置。

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

The layout file consists of header template, footer template, and a velocity variable $screen_content which renders the contents of requested velocity page:

布局文件由标题模板、页脚模板和一个速度变量$screen_content组成,它渲染了所请求的速度页面的内容。

<html>
    <head>
        <title>Velocity</title>
    </head>
    <body>
        <div>
            #parse("/fragments/header.vm")
        </div>
        <div>
            <!-- View index.vm is inserted here -->
            $screen_content
        </div>
        <div>
            #parse("/fragments/footer.vm")
        </div>
    </body>
</html>

8.3. Layout Specification in the Requested Screen

8.3.要求的屏幕中的布局规范

Layout for a particular screen can be defined as a velocity variable at the beginning of a page. That is done by putting this line in the page:

一个特定屏幕的布局可以在页面的开头定义为一个速度变量。这可以通过在页面中放入这一行来实现。

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

8.4. Layout Specification in the Request Parameter

8.4.请求参数中的布局规范

We can add a request parameter in the query string layout=MyOtherLayout.vm and VLS will find it and render the screen within that layout instead of searching for default layout.

我们可以在查询字符串中添加一个请求参数layout=MyOtherLayout.vm,VLS将找到它并在该布局中渲染屏幕,而不是搜索默认布局。

8.5. Error Screens

8.5.错误屏幕

Customized error screen can be implemented using velocity layout. VelocityLayoutServlet provides two variables $error_cause and $stack_trace to present the exception details.

定制的错误屏幕可以使用velocity layout来实现。VelocityLayoutServlet提供了两个变量$error_cause$stack_trace来呈现异常细节。

Error page can be configured in velocity.properties file:

错误页面可以在velocity.properties文件中进行配置。

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

9. Conclusion

9.结论

In this article, we have learned how Velocity is a useful tool for rendering the dynamic web pages. Also, we have seen different ways of using velocity provided servlets.

在这篇文章中,我们已经了解到Velocity是一个渲染动态网页的有用工具。此外,我们还看到了使用velocity提供的servlets的不同方法。

We also have an article focused on a Velocity configuration with Spring MVC here at Baeldung.

我们在Baeldung这里也有一篇关于Velocity配置与Spring MVC的文章

The complete code for this tutorial is available over on GitHub.

本教程的完整代码可在GitHub上找到

1.概述

1.概述

Velocity是一个基于Java的模板引擎。

它是一个开源的Web框架,旨在作为MVC架构中的一个视图组件,它为一些现有的技术(如JSP)提供了一个替代方案。

Velocity可用于生成XML文件、SQL、PostScript和大多数其他基于文本的格式。

在这篇文章中,我们将探讨如何使用它来创建动态网页。

2.Velocity如何工作

2.Velocity如何工作

Velocity的核心类是VelocityEngine

它协调了使用数据模型和速度模板来读取、解析和生成内容的整个过程。

简单地说,以下是我们在任何典型的速度应用程序中需要遵循的步骤:

  • 初始化速度引擎
  • 读取模板
  • 将数据模型放入上下文对象。

  • 将模板与上下文数据合并,并渲染视图
  • 让我们按照这些简单的步骤来看看一个例子

    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的依赖性

    3.Maven的依赖性

    为了与Velocity协同工作,我们需要在Maven项目中添加以下依赖项:

    <dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <版本>1.7</版本>
    </dependency>
    <依赖性>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <版本>2.0</版本>
    </dependency>

    这两个依赖的最新版本可以在这里找到。velocityvelocity-tools

    4.Velocity模板语言

    4.Velocity模板语言

    速度模板语言(VTL)通过使用VTL引用,提供了最简单、最干净的方式将动态内容纳入网页中。

    速度模板中的VTL引用以$开头,用于获取与该引用相关的值。VTL还提供了一组指令,可用于操纵Java代码的输出。这些指令以#开头。

    4.1.引用

    4.1.引用

    在Velocity中,有三种类型的引用,即变量、属性和方法:

    在Velocity中,有三种类型的引用。

    • 变量–使用#set指令在页面中定义,或从Java对象的字段中返回值。
      #set ($message="Hello World")
    • properties – 指向对象中的字段;它们也可以指向属性的getter方法。
      $customer.name

    • 方法–指的是Java对象上的方法。
      $customer.getName()

    每次引用产生的最终值在渲染到最终输出时都会被转换为一个字符串。

    4.2.指令

    4.2.指令

    VTL提供了一个丰富的指令集:

    VTL提供了一个丰富的指令集。

    • set – 它可以用来设置一个引用的值;这个值可以被分配给一个变量或一个属性引用。
      #set ($message = "Hello World")
      #set ($customer.name = "Brian Mcdonald")
    • 条件#if, #elseif #else 指令提供了一种基于条件检查生成内容的方法。
      #if($employee.designation == "Manager")
          <h3> 经理 </h3>
      #elseif($employee.designation == "Senior Developer")
          <h3> 高级软件工程师 </h3>
      #else
          <h3> 见习生 </h3>
      #end
    • 循环#foreach 指令允许在一个对象的集合上循环。
      <ul>
          #foreach($product in $productList)
              <li> $product </li>
          #end
      </ul>
    • include#include元素提供了将文件导入模板的能力。
      #include("one.gif", "two.txt", "three.html"...) 
    • parse#parse语句允许模板设计者导入另一个包含VTL的本地文件;然后Velocity将解析内容并渲染它。
      #parse (Template)

    • evaluate#evaluate指令可用于动态评估VTL;这允许模板在渲染时评估一个String,例如,使模板国际化。
      #set($firstName = "David")
      #set($lastName = "Johnson")
      
      #set($dynamicsource = "$firstName$lastName")
      
      #evaluate($dynamicsource)
    • break#break指令停止对当前执行范围的任何进一步渲染(即#foreach#parse
    • stop#stop指令停止任何进一步的渲染和模板的执行。
    • velocimacros#macro指令允许模板设计者定义一个重复的VTL段。
      #macro(tablerows)
          <tr>
              <td>
              </td>
          </tr>
      #end

      这个宏现在可以作为#tablerows()放到模板的任何地方:

      #macro(tablerows $color $productList)
          #foreach($product in $productList)
              <tr>
                  <td bgcolor=$color>$product.name</td>
              </tr>
          #end
      #end

    4.3.其他功能

    4.3.其他功能

  • math – 少数内置的数学函数,可以在模板中使用。
    #set($percent = $number / 100)
    #set($remainder = $dividend % $divisor)
  • 范围操作符–可与#set#foreach结合使用:
    #set($array = [0..10])
    
    #foreach($elem in $arr)
        $elem
    #end
  • 5.Velocity Servlet

    5.Velocity Servlet

    Velocity引擎的主要工作是根据模板来生成内容。

    引擎本身并不包含任何与网络有关的功能。为了实现一个网络应用,我们需要使用一个Servlet或基于Servlet的框架。

    Velocity提供了一个开箱即用的实现VelocityViewServlet,它是velocity-tools子项目的一部分。

    为了利用VelocityViewServlet提供的内置功能,我们可以从VelocityViewServlet扩展我们的Servlet,并重写handleRequest() 方法:

    public class ProductServlet extends VelocityViewServlet {
    
        ProductService service = new ProductService();
    
        @Override
        public Template handleRequest(
          HttpServletRequest请求。
          HttpServletResponse response,
          Context context) 抛出异常 {
          
            List<Product> products = service.getProducts();
            context.put("products", products);
    
            返回getTemplate("index.vm")。
        }
    }

    6.配置

    6.配置

    6.1.网络配置

    6.1.网络配置

    现在让我们看看如何在web.xml中配置VelocityViewServlet

    我们需要指定可选的初始化参数,包括velocity.propertiestoolbox.xml:

    <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>
    

    我们还需要指定这个Servlet的映射。所有对velocity模板(*.vm)的请求都需要由velocity servlet来提供服务:

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

    6.2.资源加载器

    6.2.资源加载器

    Velocity提供灵活的资源加载器系统。它允许一个或多个资源加载器同时运行:

    Velocity提供了灵活的资源加载器系统。

    • 文件资源装载器
    • JarResourceLoader
    • JarResourceLoader
    • ClassPathResourceLoader
    • ClassPathResourceLoader
    • URLResourceLoader
    • URLResourceLoader
    • DataSourceResourceLoader
    • DataSourceResourceLoader
    • WebappResourceLoader

    这些资源加载器是在velocity.properties中配置的:

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

    7.Velocity Template

    7.Velocity Template

    Velocity模板是编写所有视图生成逻辑的地方。这些页面是用Velocity模板语言(VTL)编写的:

    <html>
    ...
    <body>
    <center>
    ...
    <h2>$products.size() 产品在销售!</h2>
    <br/>
    我们很荣幸能以这些惊人的价格提供这些优质产品
    以这些惊人的价格。
    ...
    #set( $count = 1 )
    <table class="gridtable">
    <tr>
    <th>序列号</th>
    <th>产品名称</th>
    <th>价格</th>
    </tr>
    #foreach( $products中的$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.管理页面布局

    8.管理页面布局

    Velocity为基于Velocity工具的应用程序提供了一个简单的布局控制和可定制的错误屏幕。

    VelocityLayoutServlet封装了这种能力以渲染指定的布局。VelocityLayoutServletVelocityViewServlet的一个扩展。

    8.1.网络配置

    8.1.网络配置

    让我们看看如何配置VelocityLayoutServlet。该Servlet被定义为拦截对velocity模板页面的请求,布局的具体属性被定义在velocity.properties文件中:

    <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.布局模板

    8.2.布局模板

    布局模板定义了一个速度页面的典型结构。默认情况下,VelocityLayoutServlet会在布局文件夹下搜索Default.vm。重写少数属性可以改变这个位置:

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

    布局文件由页眉模板、页脚模板和一个速度变量$screen_content组成,它渲染了所请求的速度页面的内容:

    <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.要求的屏幕中的布局规范

    8.3.要求的屏幕中的布局规范

    特定屏幕的布局可以在页面的开头定义为一个速度变量。这可以通过在页面中放入这一行来实现:

    #set($layout = "MyOtherLayout.vm")/code>

    8.4.请求参数中的布局规范

    8.4.请求参数中的布局规范

    我们可以在查询字符串中添加一个请求参数layout=MyOtherLayout.vm,VLS将找到它并在该布局中渲染屏幕,而不是搜索默认布局。

    8.5.错误屏幕

    8.5.错误屏幕

    自定义的错误屏幕可以使用速度布局来实现。VelocityLayoutServlet提供了两个变量$error_cause$stack_trace来呈现异常细节。

    错误页面可以在velocity.properties文件中进行配置:

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

    9.结论

    9.结论

    在这篇文章中,我们已经了解到Velocity是一个渲染动态网页的有用工具。此外,我们还看到了使用velocity提供的servlets的不同方法。

    我们在Baeldung上也有一篇文章专注于Spring MVC的Velocity配置

    本教程的完整代码可在GitHub上获得