一、引言
展示层有三大典型技术,分别是JSP、freemaker和velocity。本文主要介绍velocity。
Velocity 主要使用场景如下:
- Web 应用:开发者在不使用 JSP 的情况下,可以用 Velocity 让 HTML 具有动态内容的特性。
- 源代码生成:Velocity 可以被用来生成 Java 代码、SQL 或者 PostScript。
- 自动 Email:很多软件的用户注册、密码提醒或者报表都是使用 Velocity 来自动生成的。
转换 xml。
二、入门
velocity是一个基于java的模板引擎,与freemarker齐名,下面写一个velocity的hello world的demo。
- maven依赖
<!--模板渲染引擎之velocity-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>${velocity.version}</version>
</dependency>
- 测试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());
}
}
- 测试结果
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 和 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 初步整合
- 添加依赖
<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>
- 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>
- 编写/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>
- 添加
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/
- 编写
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>
- 编写
webapp/vm/test/index.vm
<h2>hello world!${name}</h2>
- 编写测试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"模板。
- 编写
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>
- 完善测试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:访问localhost:8080/velocity/index
测试2:访问localhost:8080/velocity/login
可以看到,成功实现了不同页面引用和不引用公共模板的功能。