上班摸鱼吗?一文详解代码生成神器-Velocity

311 阅读6分钟

引言

“我不是在教你学坏,而是教你如何提高生产效率。” ----------- 牛顿

人类社会能够一直进步发展出现在的文明世界,最大的一个原因就是这个世界上懒人居多,懒人为了偷懒就需要提高生产效率,效率提高节省下来的时间才能创造出艺术、娱乐以及更高效率的科学技术。程序员们如何提高生产效率?创造一个自己为自己干活!

56b4d36a3b3993556bdb1f97d1e04c9.jpg

今天给大家介绍一个代码生成神器Velocity,Velocity作为一款基于Java的强大模板引擎,其拥有简洁的设计和强大的功能,新手也能很快上手。从此以后你就可以摆脱无聊且繁杂的crud代码,给自己留下诗和远方的闲暇时光。

效果展示

话不多说,以下截图是我利用Velocity写的一个代码生成的工具,支持Mysql或Pgsql两种数据库。新项目开发的时候,你只需要设计好你的表结构,在界面上填写包名、地址后缀、模块名等信息即可。剩下的事情交给Velocity,它会帮你完成从controller、service、dao的所有通用接口。

image.png

生成代码压缩包里的目录结构,前后端代码都有。

image.png

以下是适用于Mybatis框架的Velocity模板生成的代码文件。Velocity的优点之一就是它将生成代码需要的数据模型与模板解耦,所以对模板的修改可以做到非常的丝滑,数据模型做好了基本上不用做大的调整,剩下的就是DIY自己的模板文件。

image.png

也许有人会质疑说,idea中不是有很多代码生成插件吗?我认为别人写的总归没有那么灵活,我自己决定使用Velocity也是因为工作中遇到了一个开源项目需要二开,在使用别人的个性化框架期间,一个一个新建Java文件太费鼠标了,所以我才决定自己写一套模板。

Velocity

Velocity的核心理念是遵循Model-View-Controller(MVC)设计模式,它致力于将视图逻辑与业务逻辑彻底分离。它允许前端开发者使用一种专门的语法来引用预定义好的数据模型,而无需直接编写Java代码。这种设计不仅极大地简化了前端开发者的工作量,也使得后端开发者能够专注于后端逻辑的优化与实现,两者并行工作,极大地提升了开发效率。

Maven 依赖如下:

<properties>
    <velocity-tools-version>2.0</velocity-tools-version>
</properties>

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-tools</artifactId>
    <version>${velocity-tools-version}</version>
</dependency>

Velocity原理

Velocity引擎的工作流程大致如下:

  • 开发者创建包含占位符和控制结构的模板文件;

  • 这些模板通过Velocity引擎解析时,引擎会根据提供的数据模型动态替换模板中的占位符;

  • 生成所需的HTML页面、Java文件或任何其他类型的输出文件。

image.png

Hello Velocity World!

<html>
<body>
    #set( $foo = "Velocity" )
    Hello $foo World!
</body>
<html>

输出结果:

Hello Velocity World!

代码实际上很简单,声明了一个 foo 的变量,给它赋值 "Velocity", 在 body 中显示 “Hello $foo World! ”,Velocity 会将 foo 变量的值替换为 "Velocity"。

以上就是一个使用 Velocity 模板语言(VTL) 的简单应用。VTL 主要是采用引用的方式将动态的内容嵌入到输出文件里面。

建议点赞+收藏+关注,方便以后复习查阅。

截图.png

语法

VTL 语法主要分为四种:

  • 引用。通过 ${variableName} 的方式引用一个变量的属性或者方法;

  • 控制。提供一些逻辑分支或者遍历的控制命令,如 #if, #else, #elseif, #foreach 等。

  • 导入与包含。大型项目中,Velocity允许在模板之间进行内容的复用和模块化设计,如 #parse 和 #include 指令;

  • 宏定义。宏可以封装一段逻辑或输出格式,供模板多次调用以减少重复代码。

注释

单行注释:

## 我是IT果果日记。

多行注释:

#*
 我是IT果果日记
 我来自东土大唐
 要去往西天拜佛求经
*#

引用

引用的简单语法可以写作 $+标识符 的格式。标识符必须以字母开头并区分大小写,剩下的标识符由以下几种类型组成:

  • 字母 (a .. z, A .. Z)

  • 数字 (0 .. 9)

  • 下划线 ("_")

  • 连字符("-")

引用的变量都会处理成字符串对象,例如一个整形对象,Velocity 会调用其 toString() 方法将其转化为字符串。

Velocity 里的引用分为三种类型:

  • 变量

  • 属性

  • 方法

变量的引用:

$itguoguo

属性的引用:

## 这里的属性Money既可以是属性的引用,也可以是getMoney()方法的引用,由Velocity来决定是哪一个引用。
$itguoguo.Money

方法的引用:

## 方法的引用格式:$+标识符+方法体
$itguoguo.getMoney()
$itguoguo.setMoney( "1,0000,0000¥" )
$itguoguo.setWifies( ["刘亦菲", "董璇", "夏思凝"] )

此外还有两种特殊的引用:形式引用(Formal Reference Notation)安静引用(Quiet Reference Notation)

形式引用常用于引用与文本相邻的情况。例如,我要显示"I have one billionRMB",我可以写成"I have one unitRMB"。我的本意是想通过引用变量unit动态的显示RMB的数量,结果因为unitRMB相邻导致VelocityunitRMB视作了引用变量。这种情况就可以使用形式引用。像酱紫的"IhaveoneunitRMB"。我的本意是想通过引用变量unit动态的显示RMB的数量,结果因为unit和RMB相邻导致Velocity将unitRMB视作了引用变量。这种情况就可以使用形式引用。像酱紫的 "I have one {unit}RMB"。

## 形式引用
${itguoguo.getMoney()}
${itguoguo.setMoney( "1,0000,0000¥" )}
${itguoguo.setWifies( ["刘亦菲", "董璇", "夏思凝"] )}

安静引用常用于引用变量不存在的情况。

## 普通引用,Velocity返回值:$money
<input type="text" name="money" value="$money"/>
## 安静引用,Velocity返回空字符串
<input type="text" name="money" value="$!money"/>

控制

If / ElseIf / Else 代码示例

## foo为空或者false,表达式判定为假,不会输出内容
#if( $foo )
   <div>Hello ITGuoGuo!</div>
#end

## If / ElseIf / Else
#if( $foo < 5 )
    <div>up</div>
#elseif( $foo == 5 )
    <div>down</div>
#elseif( $bar == 6 )
    <div>left</div>
#else
    <div>right</div>
#end

Velocity 将在第一个为真的表达式停止并输出内容。

&& / || / ! 代码示例

#if( $foo && $bar )
   <div>逻辑与</div>
#end

#if( $foo || $bar )
    <div>逻辑或</div>
#end

#if( !$foo )
    <div>逻辑非</div>
#end

逻辑非与安静引用里的 ! 容易混淆,逻辑非的 ! 用在 之前,安静引用的!用在之前,安静引用的 ! 用在 之后。

foreach 代码示例

## names 可以是一个列表或数组
<ul>
#foreach( $name in $names )
    <li>$name</li>
#end
</ul>

## people 可以是一个键值对
<ul>
#foreach( $key in $people.keySet() )
    <li>Key: $key -> Value: $people.get($key)</li>
#end
</ul>

## Velocity 还可以在循环里使用计数缺省变量 $velocityCount,从1开始计数。
<table>
#foreach( $person in $people )
    <tr><td>$velocityCount</td>
    <td>$person.Name</td></tr>
#end
</table>

Velocity 默认为 foreach 指令提供了计数变量 $velocityCount ,从1开始计数。也可以在 velocity.properties 文件中配置计数起始的位置。例如下面的配置:

## 循环计数变量名默认 velocityCount
directive.foreach.counter.name = velocityCount

## 循环计数默认起始位从0开始
directive.foreach.counter.initial.value = 0

包含/解析

include 代码示例

#include( "ItGuoGuo.txt" )

ItGuoGuo.txt 文件将被插入到 #include 指令被定义的位置;

ItGuoGuo.txt 文件不会被 Velocity 解析,如果需要的引入文件被解析可以使用 parse 指令;

include 引入的文件必须是配置参数 TEMPLATE_ROOT 所定义目录下的,默认为当前目录。

## 引入多个文件使用逗号分隔
#include( "ItGuoGuo1.gif","ItGuoGuo2.txt","ItGuoGuo3.htm" )

## 引用文件名最好使用变量,例如 $ItGuoGuoReference
#include( "ItGuoGuo.txt", $ItGuoGuoReference )

parse 代码示例

#parse( "ItGuoGuo.vm" )

ItGuoGuo.vm 将被 Velocity 解析,即 ItGuoGuo.vm 可以是静态文件也可以是动态文件;

parse 指令只有一个参数;

parse 引入的文件必须是配置参数 TEMPLATE_ROOT 所定义目录下的,默认为当前目录。

下面是由两个 vm 文件共同完成的逻辑,首先是主文件 main.vm:

main.vm 倒计时开始
#set( $cnt = 10 )
#parse( "parsed.vm" )
main.vm 倒计时完成!

主文件申明了一个 cnt 变量,将在 parsed.vm 文件中做递减处理:

$cnt
#set( $cnt = $cnt - 1 )
#if( $cnt > 0 )
    #parse( "parsed.vm" )
#else
    parsed.vm 倒计时完成!
#end

程序首先进入 main.vm 申明一个 cnt 变量,通过引入 parsed.vm 文件递归执行递减操作,当 cnt 变量递减为0时打印 “parsed.vm 倒计时完成!” ,最后回到 main.vm 打印 “main.vm 倒计时完成!”

停止

stop 代码示例

#stop

stop 指令会停止模板引擎的执行,通常用作代码调试。

macro 指令示例

#macro( emptytd )
<tr><td></td></tr>
#end

#emptytd()

定义一个名为 emptytd 的宏,然后执行它,就会显示一行空表格。

#macro( colorrows $color $texts )
#foreach( $text in $texts )
    <tr><td bgcolor=$color>$text</td></tr>
#end
#end

定义一个名为 colorrows 的宏,第一个参数表示颜色,第二个参数表示表格里的内容。

#set( $list = ["one","two","three","four","five"] )
#set( $color = "red" )
<table>
    #tablerows( $color $list )
</table>

调用 colorrows 这个宏,传递两个参数,注意变量 list替换了list 替换了 texts,输出如下:

<table>
    <tr><td bgcolor="red">one</td></tr>
    <tr><td bgcolor="red">two</td></tr>
    <tr><td bgcolor="red">three</td></tr>
    <tr><td bgcolor="red">four</td></tr>
    <tr><td bgcolor="red">five</td></tr>
</table>

宏一般用来在多个模板中共享,这样可以减少模板内的重复工作量,也减少了出错的机率。

转义

Velocity 的指令使用 $ 和 # 开头,如果在模板里需要使用这两个特殊字符,需要做转义处理。

I have $1000000000!
I have $money!

1000000000不需要做转义,因为引用变量名必须是大写或这小写字母开头。1000000000 不需要做转义,因为引用变量名必须是大写或这小写字母开头。money 是否需要转义要看你想输出 money 这个变量还是 “money” 这个字符串。

#set( $money = "1,000,000,000" )
I have $money!
I have \$money!

输出结果如下:

I have 1,000,000,000!
I have $money!

还有一种情况是引用变量的值需要转义,可以使用 Velocity 的扩展工具 EscapeTool,Maven 依赖如下:

<dependency>
    <groupId>org.apache.velocity.tools</groupId>
    <artifactId>velocity-tools-generic</artifactId>
    <version>3.1</version>
</dependency>

代码生成

image.png

前面提到过 Velocity 生成代码的原理是将数据模型和模板合并,输出指定格式的文件。下面我们就来自己动手利用 Velocity 写一个生成代码的小工具。

数据模型

首先需要定义自己的数据模型,因为我们写的这个工具是用来生成 crud 代码的,所以数据模型里的字段主要就是表的元信息。表的元信息实体类如下:

image.png

列的元信息实体类如下:

image.png

定义好数据模型之后,我们需要调用数据库查找这些信息。以 Mysql 为例:

image.png

模板文件

多个模板文件可以共用一套数据模型,当前我们以生成实体类文件为例,实体模板如下:

image.png

合并生成

最后是将数据模型合并到模板文件,生成代码文件:

image.png

参考

velocity中文文档 wizardforcel.gitbooks.io/velocity-do…

建议点赞+收藏+关注,方便以后复习查阅。

截图.png