Velocity Engine基礎


前言:如果博客看的不是很方便,可以下載.zip壓縮包到本地自行學習:

Velocity學習Markdown文檔下載

一. velocity簡介

1. velocity簡介

Velocity是一個基於Java的模板引擎,可以通過特定的語法獲取在java對象的數據 , 填充到模板中,從而實現界面和java代碼的分離!
image

2. 應用場景

  • Web應用程序 : 作為為應用程序的視圖, 展示數據。
  • 源代碼生成 : Velocity可用於基於模板生成Java源代碼
  • 自動電子郵件 : 網站注冊 , 認證等的電子郵件模板
  • 網頁靜態化 : 基於velocity模板 , 生成靜態網頁

3. velocity 組成結構

image
Velocity主要分為app、context、runtime和一些輔助util幾個部分。

  • app模塊 : 主要封裝了一些接口 , 暴露給使用者使用。主要有兩個類,分別是Velocity(單例)和VelocityEngine。

  • Context模塊 : 主要封裝了模板渲染需要的變量

  • Runtime模塊 : 整個Velocity的核心模塊,Runtime模塊會將加載的模板解析成語法樹,Velocity調用mergeTemplate方法時會渲染整棵樹,並輸出最終的渲染結果。

  • RuntimeInstance類為整個Velocity渲染提供了一個單例模式,拿到了這個實例就可以完成渲染過程了。

二. 快速入門

1. 需求分析

使用velocity定義html模板 , 將動態數據填充到模板中 , 形成一個完整的html頁面

2. 步驟分析

  1. 創建項目(maven)
  2. 引入依賴
  3. 定義模板
  4. 輸出html

3. 代碼實現

3.1 創建工程

image

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的工作原理。
image

三. 基礎語法

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 步驟分析

  1. 創建項目
  2. 導入依賴
  3. 編寫模板
  4. 生成代碼

4.3 代碼實現

4.3.1 創建項目

image

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM