Java中使用velocity实现模板渲染

一、引言

展示层有三大典型技术,分别是JSP、freemaker和velocity。本文主要介绍velocity。

Velocity 主要使用场景如下:

  • Web 应用:开发者在不使用 JSP 的情况下,可以用 Velocity 让 HTML 具有动态内容的特性。
  • 源代码生成:Velocity 可以被用来生成 Java 代码、SQL 或者 PostScript。
  • 自动 Email:很多软件的用户注册、密码提醒或者报表都是使用 Velocity 来自动生成的。
    转换 xml。

二、入门

velocity是一个基于java的模板引擎,与freemarker齐名,下面写一个velocity的hello world的demo。

  1. maven依赖
<!--模板渲染引擎之velocity-->
<dependency>
 <groupId>org.apache.velocity</groupId>
 <artifactId>velocity</artifactId>
 <version>${velocity.version}</version>
</dependency>
  1. 测试servelt
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * ==Velocity之Hello Wordld入门程序==
 * <p>
 * 首先,我们在代码中初始化了VelocityEngine这个模板引擎,对其设置参数进行初始化,
 * 指定使用ClasspathResourceLoader来加载vm文件。然后我们就可以往VelocityContext这个Velocity
 * 容器中存放对象了,在vm文件中我们可以取出这些变量,从而进行模板输出。
 */
public class VelocityTest {

    private static final String VM_PATH = "template/velocity/helloworld.vm";

    public static void main(String[] args) {
        // 初始化模板引擎
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        velocityEngine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        velocityEngine.init();

        // 获取模板文件
        Template template = velocityEngine.getTemplate(VM_PATH);

        // 设置变量,velocityContext是一个类似map的结构
        VelocityContext velocityContext = new VelocityContext();
        velocityContext.put("name", "world");
        List<String> list = new ArrayList<String>();
        list.add("jack");
        list.add("kitty");
        velocityContext.put("list", list);

        // 输出渲染后的结果
        StringWriter stringWriter = new StringWriter();
        template.merge(velocityContext, stringWriter);
        System.out.println(stringWriter.toString());
    }
}
  1. 测试结果
hello world
1: This is jack
2: This is kitty

二、语法

2.1 变量

和我们所熟知的其他编程语言一样,Velocity 也可以在模板文件中有变量的概念。

变量定义

#set($name =“velocity”)

等号后面的字符串 Velocity 引擎将重新解析,例如出现以$开始的字符串时,将做变量的替换。

 #set($hello =“hello $name”)

上面的这个等式将会给 $hello 赋值为“hello velocity”

变量的使用
在模板文件中使用name 或者{name} 来使用定义的变量。推荐使用 {name} 这种格式,因为在模板中同时可能定义了类似name 和 names 的两个变量,如果不选用大括号的话,引擎就没有办法正确识别names 这个变量。

#set($name =“ricky”)

Welcome $name to velocity.com

2.2 循环

在 Velocity 中循环语句的语法结构如下:

#foreach($element in $list)
 This is $element
 $velocityCount
#end

Velocity 引擎会将 list 中的值循环赋给 element 变量,同时会创建一个 $velocityCount 的变量作为计数,从 1 开始,每次循环都会加 1.

2.3 条件语句

条件语句的语法如下:

#if(condition)
#elseif(condition)
#else
#end

2.4 关系操作符

Velocity 引擎提供了 AND、OR 和 NOT 操作符,分别对应&&、||和! 例如:

#if($foo && $bar)
#end

2.5 宏

Velocity 中的宏可以理解为函数定义。定义的语法如下:

#macro(macroName arg1 arg2 …)
#end

调用这个宏的语法是:

#macroName(arg1 arg2 …)

这里的参数之间使用空格隔开,下面是定义和使用 Velocity 宏的例子:

#macro(sayHello $name)
hello $name
#end
#sayHello(“velocity”)

输出的结果为 hello velocity

2.6 内嵌和引用

#parse#include 指令的功能都是在外部引用文件,二者的区别是:

  • #parse会将引用的内容当成类似于源码文件,会将内容在引入的地方进行解析
  • #include是将引入文件当成资源文件,会将引入内容原封不动地以文本输出

下面分别看两个例子:

案例1:

## foo.vm 文件:
#set($name =“velocity”)
## parse.vm文件:
#parse(“foo.vm”)

输出结果为:

velocity

案例2:

## foo.vm 文件:
#set($name =“velocity”)
## include.vm:
#include(“foo.vm”)

输出结果为:

#set($name =“velocity”)

三、Spring整合velocity进行页面展示

3.1 初步整合

  1. 添加依赖
<dependency>
    <groupId>org.apache.velocity</groupId>
     <artifactId>velocity</artifactId><!-- velocity与velocity-tools缺一不可 -->
     <version>1.6.2</version>
</dependency>
<dependency>
     <groupId>org.apache.velocity</groupId>
     <artifactId>velocity-tools</artifactId>
     <version>2.0</version>
</dependency>
  1. web.xml
<!-- 2. spring mvc核心:分发servlet -->
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- spring mvc的配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
  1. 编写/resources/spring-mvc.xml
<!--InternalResourceViewResolver视图配置-->
    <!--<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
    <!--<property name="prefix" value="/WEB-INF/pages/"/>-->
    <!--<property name="suffix" value=".jsp"/>-->
    <!--</bean>-->

    <!--velocity模板配置-->
    <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath" value="WEB-INF/views/"/><!-- 这个好像没效果,velocity.properties中的配置起作用 -->
        <property name="configLocation" value="classpath:velocity.properties"/>
        <property name="velocityProperties">
            <props>
                <prop key="input.encoding">UTF-8</prop>
                <prop key="output.encoding">UTF-8</prop>
            </props>
        </property>
    </bean>

    <!--VelocityViewResolver视图配置-->
    <!--<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">-->
    <!--<property name="suffix" value=".vm"/>-->
    <!--<property name="prefix" value=""/>-->
    <!--<property name="contentType" value="text/html;charset=UTF-8"/>-->
    <!--</bean>-->

    <!--
    layout功能
    上面我们使用的是VelocityLayoutWithCommonToolsViewResolver这个对象做为springmvc的视图解析器,这个对象是集成了layout功能和tool功能为一体的解析器。
    对于一个WEB应用来说,页面的基本结构往往是固定的,页面中的很多部分,例如页面统一的头部、尾部和菜单,我们一般是不太需要变化的,各个页面基本一致,变化的往往是页面的具体内容部分,这样,布局(layout)功能的使用,就能大大减化前端页面的复杂性了。这里简单介绍一下如何使用velocity框架来实现页面的布局。
    我们知道,要在spring框架中配置velocity,一般需要配置两个bean,一个是velocityConfig, 别一个则是viewResolver。先来看velocityConfig的配置,基本配置如下所示,这里和不使用布局的配置方式没有任何差别。
    1. 一般情况下,当我们不使用velocity的布局功能时,我们一般会把viewResolver的class配置为:
    org.springframework.web.servlet.view.velocity.VelocityViewResolver,当需要使用布局功能的时候,viewResolver的class需要配置为:
    org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver,顾名思义,从命名中我们就能看出来这个viewResolver是支持layout功能的。
    2. 不同的是,这里多了一个layoutUrl的属性配置:这个配置是设置你的layout文件的存在路径,需要注意的是,这个路径不是相对于webapp路径来的,而是相对于velocityConfig配置中的resourceLoaderPath属性配置的路径(resourceLoaderPath的路径是相对于webapp的路径)。
    普通的页面实现:比如我们要输出一个带带固定页头和页尾的页面,内容就是screen_content的值,最后使用普通解析器页面如下:
    -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver">
        <property name="suffix" value=".vm"/><!-- 后缀可以是任何东西,只要跟你的文件后缀名相同,控制器进行视图重定向就能找到这个文件 -->
        <property name="prefix" value=""/>
        <property name="contentType" value="text/html;charset=UTF-8"/>
        <property name="layoutUrl" value="layout/layout.vm"/>
    </bean>
  1. 添加resources/velocity.properties
## 设置模板文件加载器,webapp从应用根目录加载
resource.loader = webapp
webapp.resource.loader.class = org.apache.velocity.tools.view.WebappResourceLoader
## 模板路径,根目录下的vm文件夹,比如访问http://localhost:8080/velocity/t.htm,则选中的模板路径是webapp/vm/t.htm,如果没配置,默认是模板路径是webapp/t.htm
webapp.resource.loader.path = /vm
## 设置编码,不设置会中文乱码
input.encoding = UTF-8
output.encoding = UTF-8
tools.view.servlet.layout.default.template=default.vm
# 默认的布局文件位置
tools.view.servlet.layout.directory=/WEB-INF/layout/ 
  1. 编写webapp/vm/layout/layout.vm
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    </head>

    <body>
        <div>header</div>

        <div>
            $screen_content
        </div>

        <div>footer</div>
    </body>
</html>
  1. 编写webapp/vm/test/index.vm
<h2>hello world!${name}</h2>
  1. 编写测试Controller
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * velocity与Spring MVC集成作为展示页面Demo
 */
@Controller
@RequestMapping(value = "/velocity")
public class MyVelocityTestController {

    /**
     * 模拟首页
     *
     * @param model
     * @return
     */
    @RequestMapping(value = "/index")
    public String index(Model model) {
        String name = "tester";
        model.addAttribute("name", name);
        return "test/index";
    }

3.2 velocity选择不同的模板

系统中的登录页面所使用的模板与其他普通页面使用的模板不同,或者是某些页面需要引用不同的模板。
先按照上节velocity(4) 的步骤配置好,通过以上配置后普通页面velocity会自动套用layout/default.vm模板。如果登录页面需套用自己独特的模板则如下可以在登录页面中添加:#set($layout="login_layout.vm"),则登录页面将套用"login_layout.vm"模板。

  1. 编写webapp/vm/layout/empty.vm
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    </head>

    <body>
        $screen_content
    </body>
</html>

webapp/test/login.vm

#set($layout="layout/empty.vm")
<h2>This is the login page</h2>
  1. 完善测试controller
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * velocity与Spring MVC集成作为展示页面Demo
 */
@Controller
@RequestMapping(value = "/velocity")
public class MyVelocityTestController {

    /**
     * 模拟首页
     *
     * @param model
     * @return
     */
    @RequestMapping(value = "/index")
    public String index(Model model) {
        String name = "tester";
        model.addAttribute("name", name);
        return "test/index";
    }

    /**
     * 模拟使用不一样模板的登录页
     *
     * @param model
     * @return
     */
    @RequestMapping(value = "/login")
    public String login(Model model) {
        return "test/login";
    }
}
  1. 测试
    测试1:访问localhost:8080/velocity/index
test1

测试2:访问localhost:8080/velocity/login

test2

可以看到,成功实现了不同页面引用和不引用公共模板的功能。

四、参考资料

  1. velocity文集
  2. Velocity 快速入门教程
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容