前言:如果博客看的不是很方便,可以下載.zip壓縮包到本地自行學習:
一. velocity簡介
1. velocity簡介
Velocity是一個基於Java的模板引擎,可以通過特定的語法獲取在java對象的數據 , 填充到模板中,從而實現界面和java代碼的分離!
2. 應用場景
- Web應用程序 : 作為為應用程序的視圖, 展示數據。
- 源代碼生成 : Velocity可用於基於模板生成Java源代碼
- 自動電子郵件 : 網站注冊 , 認證等的電子郵件模板
- 網頁靜態化 : 基於velocity模板 , 生成靜態網頁
3. velocity 組成結構
Velocity主要分為app、context、runtime和一些輔助util幾個部分。
-
app模塊 : 主要封裝了一些接口 , 暴露給使用者使用。主要有兩個類,分別是Velocity(單例)和VelocityEngine。
-
Context模塊 : 主要封裝了模板渲染需要的變量
-
Runtime模塊 : 整個Velocity的核心模塊,Runtime模塊會將加載的模板解析成語法樹,Velocity調用mergeTemplate方法時會渲染整棵樹,並輸出最終的渲染結果。
-
RuntimeInstance類為整個Velocity渲染提供了一個單例模式,拿到了這個實例就可以完成渲染過程了。
二. 快速入門
1. 需求分析
使用velocity定義html模板 , 將動態數據填充到模板中 , 形成一個完整的html頁面
2. 步驟分析
- 創建項目(maven)
- 引入依賴
- 定義模板
- 輸出html
3. 代碼實現
3.1 創建工程
3.2 引入坐標
<dependencies>
<dependency>
<groupid>org.apache.velocity</groupid>
<artifactid>velocity-engine-core</artifactid>
<version>2.2</version>
</dependency>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3.3 編寫模板
在項目resources
目錄下創建模板文件
<meta charset="UTF-8">
<title>Title</title>
hello , ${name} !
3.4 輸出結果
@Test
public void test1() throws IOException {
//設置velocity資源加載器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//創建Velocity容器
VelocityContext context = new VelocityContext();
context.put("name", "zhangsan");
//加載模板
Template tpl = Velocity.getTemplate("vms/demo1.vm", "UTF-8");
FileWriter fw = new FileWriter("D:\\work\\workspace\\velocity\\velocity_01\\src\\main\\resources\\html\\demo1.html");
//合並數據到模板
tpl.merge(context, fw);
//釋放資源
fw.close();
}
4. 運行原理
Velocity解決了如何在后台程序和網頁之間傳遞數據的問題,后台代碼和視圖之間相互獨立,一方的修改不影響另一方 .
他們之間是通過環境變量(Context)來實現的,網頁制作一方和后台程序一方相互約定好對所傳遞變量的命名約定,比如上個程序例子中的site, name變量,它們在網頁上就是$name ,$site 。
只要雙方約定好了變量名字,那么雙方就可以獨立工作了。無論頁面如何變化,只要變量名不變,那么后台程序就無需改動,前台網頁也可以任意由網頁制作人員修改。這就是Velocity的工作原理。
三. 基礎語法
3.1 VTL介紹
Velocity Template Language (VTL) , 是Velocity 中提供的一種模版語言 , 旨在提供最簡單和最干凈的方法來將動態內容合並到網頁中。簡單來說VTL可以將程序中的動態數展示到網頁中
VTL的語句分為4大類:注釋 , 非解析內容 ** , 引用和指令**。
3.2 VTL注釋
3.2.1 語法
1. 行注釋
## 行注釋內容
2. 塊注釋
#*
塊注釋內容1
塊注釋內容2
*#
3. 文檔注釋
#**
文檔注釋內容1
文檔注釋內容2
*#
3.2.1 示例
<meta charset="UTF-8">
<title>Title</title>
## 我是行注釋
#*
* 我是塊注釋
* 呵呵呵
* *#
#**
* 我是文檔注釋
*
* *#
hello , ${name} !
3.3 非解析內容
所謂非解析內容也就是不會被引擎解析的內容。
3.3.1 語法
#[[
非解析內容1
非解析內容2
]]#
3.3.2 示例
<meta charset="UTF-8">
<title>Title</title>
hello , ${name} !
<h1>非解析內容</h1>
#[[
直接輸出的內容1
直接輸出的內容2
${name}
]]#
3.4 引用
3.4.1 變量引用
引用語句就是對引擎上下文對象中的屬性進行操作。語法方面分為常規語法($屬性
)和正規語法(${屬性})。
語法
$變量名, 若上下文中沒有對應的變量,則輸出字符串"$變量名"
${變量名},若上下文中沒有對應的變量,則輸出字符串"${變量名}"
$!變量名, 若上下文中沒有對應的變量,則輸出空字符串""
$!{變量名}, 若上下文中沒有對應的變量,則輸出空字符串""
示例
<meta charset="UTF-8">
<title>Title</title>
<h1>引用變量</h1>
常規語法 : $name
正規語法 : ${name}
## 如果獲取的變量不存在, 表達式會原樣展示 , 如果不想展示 , 可以使用 $!變量名
## 以下寫法的含義代表么如果有變量, 那么獲取變量值展示, 沒有變量展示""
常規語法 : $!name
正規語法 : $!{name}
3.4.2 屬性引用
語法
$變量名.屬性, 若上下文中沒有對應的變量,則輸出字符串"$變量名.屬性"
${變量名.屬性} 若上下文中沒有對應的變量,則輸出字符串"${變量名.屬性}"
$!變量名.屬性 若上下文中沒有對應的變量,則輸出字符串""
$!{變量名.屬性} 若上下文中沒有對應的變量,則輸出字符串""
示例
<meta charset="UTF-8">
<title>Title</title>
<h1>引用屬性</h1>
常規語法 : $user.username --- $user.password
正規語法 : ${user.username} --- ${user.password}
正規語法 : ${user.email} --- ${user.email}
正規語法 : $!{user.email} --- $!{user.email}
3.4.3 方法引用
方法引用實際就是指方法調用操作,關注點返回值和參數 , 方法的返回值將輸出到最終結果中
語法
$變量名.方法([入參1[, 入參2]*]?), 常規寫法
${變量名.方法([入參1[, 入參2]*]?)}, 正規寫法
$!變量名.方法([入參1[, 入參2]*]?), 常規寫法
$!{變量名.方法([入參1[, 入參2]*]?)}, 正規寫法
示例
<meta charset="UTF-8">
<title>Title</title>
<h1>引用屬性</h1>
$str.split(" ")
${str.split(" ")}
$time.getTime()
${time.getTime()}
3.5 指令
指令主要用於定義重用模塊、引入外部資源、流程控制。指令以 #
作為起始字符。
3.5.1 流程控制
#set
作用 : 在頁面中聲明定義變量
語法: #set($變量 = 值)
示例 :
<meta charset="UTF-8">
<title>Title</title>
<h1>set指令</h1>
#set($str = "hello world")
#set($int = 1)
#set($arr = [1,2])
#set($boolean = true)
#set($map = {"key1":"value1", "key2":"value2"})
## 在字符串中也可以引用之前定義過的變量
#set($str2 = "$str , how are you !")
#set($str3 = '$str , how are you !')
<h1>獲取set指令定義的變量</h1>
${str}
${int}
${arr}
${boolean}
${map.key1}--${map.key2}
${str2}
${str3}
#if/#elseif/#else
作用 : 進行邏輯判斷
語法 :
#if(判斷條件)
.........
#elseif(判斷條件)
.........
#else
.........
#end
示例 :
<meta charset="UTF-8">
<title>Title</title>
<h1>if/elseif/else指令</h1>
#set($language="PHP")
#if($language.equals("JAVA"))
java開發工程師
#elseif($language.equals("PHP"))
php開發工程師
#else
開發工程師
#end
#foreach
作用 : 遍歷循環數組或者集合
格式:
#foreach($item in $items)
..........
[#break]
#end
> - $items : 需要遍歷的對象或者集合
> - 如果items的類型為map集合, 那么遍歷的是map的value
> - $item : 變量名稱, 代表遍歷的每一項
> - #break : 退出循環
> - 內置屬性 :
> - $foreach.index : 獲取遍歷的索引 , 從0開始
> - $foreach.count : 獲取遍歷的次數 , 從1開始
示例 :
<meta charset="UTF-8">
<title>Title</title>
<h1>遍歷數組</h1>
#foreach($str in $hobbies)
${foreach.index} -- ${str} <br>
#end
<h1>變量對象集合</h1>
#foreach($user in $users)
#end
<table border="1px" align="center">
<tbody><tr>
<td>編號</td>
<td>用戶名</td>
<td>密碼</td>
<td>郵箱</td>
<td>年齡</td>
<td>操作</td>
</tr><tr>
<td>${foreach.index}</td>
<td>${user.username}</td>
<td>${user.password}</td>
<td>${user.email}</td>
<td>${user.age}</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr></tbody></table>
<h1>遍歷map集合</h1>
<h2>遍歷值</h2>
#foreach($value in $map)
$value
#end
<h2>遍歷鍵值對</h2>
#foreach($entry in $map.entrySet())
$entry.key -- $entry.value
#end
3.5.2 引入資源
#include
作用 : 引入外部資源 , 引入的資源不會被引擎所解析
語法 : #include(resource)
- resource可以為單引號或雙引號的字符串,也可以為$變量,內容為外部資源路徑。
- 注意 : 路徑如果為相對路徑,則以引擎配置的文件加載器加載路徑作為參考
示例 :
<meta charset="UTF-8">
<title>Title</title>
#include("demo8.vm")
#parse
作用 : 引入外部資源 , 引入的資源將被引擎所解析
語法 : #parse(resource)
- resource可以為單引號或雙引號的字符串,也可以為$變量,內容為外部資源路徑。
- 注意 : 路徑如果為相對路徑,則以引擎配置的文件加載器加載路徑作為參考系
示例 :
<meta charset="UTF-8">
<title>Title</title>
#parse("demo8.vm")
#define
作用 : 定義重用模塊(不帶參數)
語法 :
#define($模塊名稱)
模塊內容
#end
示例 :
<meta charset="UTF-8">
<title>Title</title>
<h1>定義模塊</h1>
#define($table)
<table border="1px" align="center">
<tbody><tr>
<td>編號</td>
<td>用戶名</td>
<td>密碼</td>
<td>郵箱</td>
<td>年齡</td>
<td>操作</td>
</tr>
<tr>
<td>0</td>
<td>zhangsan</td>
<td>123456</td>
<td>zhangsan@qq.com</td>
<td>18</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr>
<tr>
<td>1</td>
<td>lisi</td>
<td>123456</td>
<td>zhangsan@qq.com</td>
<td>18</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr>
<tr>
<td>2</td>
<td>wangwu</td>
<td>123456</td>
<td>zhangsan@qq.com</td>
<td>18</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr>
<tr>
<td>3</td>
<td>tianqi</td>
<td>123456</td>
<td>zhangsan@qq.com</td>
<td>18</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr>
</tbody></table>
#end
<h1>使用模塊</h1>
$table
$table
$table
#evaluate
作用 : 動態計算 , 動態計算可以讓我們在字符串中使用變量
語法 : #evalute("計算語句")
示例 :
<meta charset="UTF-8">
<title>Title</title>
<h1>動態計算</h1>
#set($name = "over")
#evaluate("#if($name=='over') over #else not over #end")
#if($name=='over')
over
#else
not over
#end
3.5.3 宏指令
作用 : 定義重用模塊(可帶參數)
語法 :
定義語法
#macro(宏名 [$arg]?)
.....
#end
調用語法
#宏名([$arg]?)
**示例 : **
<meta charset="UTF-8">
<title>Title</title>
<h1>定義宏</h1>
#macro(table $list)
#foreach($item in $list)
#end
<table border="1px">
<tbody><tr>
<td>編號</td>
<td>用戶名</td>
<td>密碼</td>
<td>郵箱</td>
<td>年齡</td>
<td>操作</td>
</tr><tr>
<td>${foreach.count}</td>
<td>${item.username}</td>
<td>${item.password}</td>
<td>${item.email}</td>
<td>${item.age}</td>
<td>
<a href="">編輯</a>
<a href="">刪除</a>
</td>
</tr></tbody></table>
#end
<h1>調用宏</h1>
#table($users)
四. 綜合案例
4.1 需求分析
在實際項目開發過程中, 編寫基礎的CRUD操作代碼, 往往會花費我們大量的時間 , 而且這些CRUD代碼的基礎結構基本上是固定的 , 如果能有一個代碼生成器 , 能夠幫助我們把這些代碼生成出來 , 我們就可以節省出大量的時間關注核心業務代碼的開發, 大大提高了我們的開發效率 !
需求 : 使用velocity實現一個簡單的代碼生成器 , 生成項目開發過程中的基礎CRUD代碼
4.2 步驟分析
- 創建項目
- 導入依賴
- 編寫模板
- 生成代碼
4.3 代碼實現
4.3.1 創建項目
4.3.2 導入依賴
<dependency>
<groupid>org.apache.velocity</groupid>
<artifactid>velocity-engine-core</artifactid>
<version>2.2</version>
</dependency>
4.3.3 編寫模板
一般我們的項目開發將項目分為三層 , 我們的代碼生成器就基於傳統的三層架構生成代碼 , 所以我們需要為每一層的每一個類創建模板 , 所以需要有如下模板 :
- Controller.java.vm : 控制層模板
- Service.java.vm : 業務層接口模板
- ServiceImpl.java.vm : 業務層實現模板
- Dao.java.vm : 數據服務層模板(數據訪問層基於通用Mpper實現)
Controller.java.vm
package ${package}.controller;
import ${package}.pojo.${className};
import ${package}.service.${className}Service;
import ${package}.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/${classname}")
public class ${className}Controller {
@Autowired
private ${className}Service ${classname}Service ;
/**
* 查詢列表
* @return
*/
@RequestMapping("/list")
public Result list(){
List<${className}> ${classname}s = null;
try {
${classname}s = ${classname}Service.list();
return Result.ok(${classname}s);
} catch (Exception e) {
e.printStackTrace();
return Result.error("查詢數據異常");
}
}
/**
* 保存
* @param ${classname}
* @return
*/
@RequestMapping("/save")
public Result save(@RequestBody ${className} ${classname}){
try {
${classname}Service.save(${classname});
return Result.ok("新增數據成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("新增數據異常");
}
}
/**
* 更新
* @param ${classname}
* @return
*/
@RequestMapping("/update")
public Result update(@RequestBody ${className} ${classname}){
try {
${classname}Service.update(${classname});
return Result.ok("修改數據成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("修改數據異常");
}
}
/**
* 刪除
* @param ids
* @return
*/
@RequestMapping("/delete")
public Result delete(@RequestBody Integer[] ids){
try {
${classname}Service.delete(ids);
return Result.ok("刪除數據成功");
} catch (Exception e) {
e.printStackTrace();
return Result.error("刪除數據異常");
}
}
}
Service.java.vm
package ${package}.service;
import com.itheima.pojo.${className};
import java.util.List;
import java.util.Map;
public interface ${className}Service {
/**
* 查詢數據列表
* @return
*/
List<${className}> list();
/**
* 保存數據
* @param ${classname}
*/
void save(${className} ${classname});
/**
* 更新數據
* @param ${classname}
*/
void update(${className} ${classname});
/**
* 刪除數據
* @param ids
*/
void delete(Integer[] ids);
}
ServiceImpl.java.vm
package ${package}.service.impl;
import ${package}.dao.${className}Dao;
import ${package}.pojo.${className};
import ${package}.service.${className}Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class ${className}ServiceImpl implements ${className}Service {
@Autowired
private ${className}Dao ${classname}Dao ;
public List<${className}> list() {
return ${classname}Dao.selectAll();
}
public void save(${className} ${classname}) {
${classname}Dao.insert(${classname});
}
public void update(${className} ${classname}) {
${classname}Dao.updateByPrimaryKey(${classname});
}
public void delete(Integer[] ids) {
Stream.of(ids).forEach(${classname}Dao::deleteByPrimaryKey);
}
}
Dao.java.vm
package ${package}.dao;
import com.itheima.pojo.${className};
import tk.mybatis.mapper.common.Mapper;
public interface ${className}Dao extends Mapper<${className}> {
}
4.3.4 生成代碼
我們可以封裝一個生成代碼的工具類 , 后期生成代碼運行工具類即可
package com.itheima.utils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 代碼生成器 工具類
*/
public class GenUtils {
private static String currentTableName;
public static List<string> getTemplates() {
List<string> templates = new ArrayList<string>();
templates.add("vms/Controller.java.vm");
templates.add("vms/Service.java.vm");
templates.add("vms/ServiceImpl.java.vm");
templates.add("vms/Dao.java.vm");
return templates;
}
/**
* 生成代碼
*/
public static void generatorCode(Map<string, object=""> data, List<string> templates, ZipOutputStream zip) {
//設置velocity資源加載器
Properties prop = new Properties();
prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
Velocity.init(prop);
//封裝模板數據
VelocityContext context = new VelocityContext(data);
//獲取模板列表
for (String template : templates) {
//渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, "UTF-8");
tpl.merge(context, sw);
try {
//添加到zip
zip.putNextEntry(new ZipEntry(getFileName(template, data.get("className").toString(), data.get("package").toString())));
IOUtils.write(sw.toString(), zip, "UTF-8");
IOUtils.closeQuietly(sw);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 獲取文件名 , 每個文件所在包都不一樣, 在磁盤上的文件名幾路徑也各不相同
*/
public static String getFileName(String template, String className,String packageName) {
String packagePath = "main" + File.separator + "java" + File.separator;
if (StringUtils.isNotBlank(packageName)) {
packagePath += packageName.replace(".", File.separator) + File.separator;
}
if (template.contains("Dao.java.vm")) {
return packagePath + "dao" + File.separator + className + "Dao.java";
}
if (template.contains("Service.java.vm")) {
return packagePath + "service" + File.separator + className + "Service.java";
}
if (template.contains("ServiceImpl.java.vm")) {
return packagePath + "service" + File.separator + "impl" + File.separator + className + "ServiceImpl.java";
}
if (template.contains("Controller.java.vm")) {
return packagePath + "controller" + File.separator + className + "Controller.java";
}
return null;
}
}
4.3.5 運行測試
public class GeneratorCodeTest {
public static void main(String[] args) throws IOException {
Map<string,object> data = new HashMap<string,object>();
data.put("className","Product");
data.put("classname","product");
data.put("package","com.itheima");
File file = new File("D:\\Users\\Desktop\\code.zip");
FileOutputStream outputStream = new FileOutputStream(file);
ZipOutputStream zip = new ZipOutputStream(outputStream);
GenUtils.generatorCode(data,GenUtils.getTemplates(),zip);
zip.close();
}
}
運行完畢之后, 可以看到輸出路徑下回自動生成一個壓縮文件 , 解壓之后將里面的代碼copy到我們的項目之中即可 ! </string,object></string,object></string,>