1.velocity简介
velocity是一个基于Java的模板引擎,可以通过特定的语法获取在Java对象的数据,填充到模板中,从而实现界面和Java代码的分离。这意味着可以使用velocity替代jsp的开发模式了,这使得前端开发人员可以和 Java 程序开发人员同步开发一个遵循 MVC 架构的 web 站点,在实际应用中,velocity还可以应用于很多其他的场景。
2.应用场景
web应用程序:作为应用程序的视图,展示数据。
源代码生成:velocity可用于基于模板生成Java源代码。
自动电子邮件:网站注册,认证等的电子邮件模板。
网页静态化:基于velocity模板,生成静态网页。
3.velocity组成结构
velocity主要分为app、context、runtime和一些辅助util几个部分
app模块:主要封装了一些接口,暴露给使用者使用,主要有两个类,分别是Velocity(单例)和VelocityEngine
context模块:主要封装了模板渲染需要的变量
runtime模块:整个velocity的核心模块,runtime模块会将加载的模块解析成语法树,velocity调用mergeTemplate方法时会渲染整棵树,并输出最终的渲染结果
runtimeInstance类为整个velocity渲染提供了一个单例模式,拿到了这个实例就可以完成渲染过程了
4.快速入门
4.1 需求分析
使用velocity定义HTML模板,将动态数据填充到模板中,形成一个完整的HTML页面
4.2 步骤分析
(1)创建项目
(2)引入依赖
(3)定义模板
(4)输出html
4.3 velocity入门程序
创建maven项目,然后引入如下依赖
<!--velocity--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.0</version> </dependency>
在templates目录下新建一个模板文件 demo1.vm,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello,${name}!
</body>
</html>
编写测试类,使用velocity生成html文件
@Test public void test1() throws IOException { // 1.设置velocity的资源加载器 Properties properties=new Properties(); properties.put("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); // 2.初始化velocity引擎 Velocity.init(properties); // 3.创建velocity容器 VelocityContext context=new VelocityContext(); context.put("name","张三"); // 4.加载模板文件 Template template = Velocity.getTemplate("templates\demo1.vm", "utf-8"); // 5.合并数据到模板 FileWriter fw=new FileWriter("D:\idea_code\cat-movie-main\cat-movie-main\movie-server\src\main\resources\static\demo1.html"); template.merge(context,fw); // 6.释放资源 fw.close(); }
执行单元测试,就可以看到static目录下生成了demo1.html,并且数据显示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello,张三!
</body>
</html>
5.基础语法
5.1 VTL介绍
Velocity Template Language(VTL),是velocity中提供的一种模板语言,旨在提供最简单和最干净的方法来将动态内容合并到网页中,简单来说VTL可以将程序的动态数展示到网页中。
VTL语法分为四大类:注释、非解析内容、引用和指令
5.2 注释
注释的内容不会被输出
1.单行注释
## 行注释内容
2.多行注释
#* *多行注释1 *多行注释2 *#
3.文档注释
#**
*文档注释
*
*#
5.3 非解析内容
非解析内容会原样输出
#[[
非解析内容${name}
]]#
5.4 引用
5.4.1 变量引入
引用语法就是对引擎上下文对象中的属性进行操作,语法方面分为常规语法($属性)和正规语法(${属性})
$变量名,若上下文中没有对应的变量,则输出字符串$变量名
${变量名},若上下文中没有对应的变量,则输出字符串${变量名}
$!变量名,若上下文中没有对应的变量,则输出空字符串""
$!{变量名},若上下文中没有对应的变量,则输出空字符串""
5.4.2 属性引用
$变量名.属性,若上下文中没有对应的变量,则输出字符串$变量名.属性
${变量名.属性},若上下文中没有对应的变量,则输出字符串${变量名.属性}
$!变量名.属性,若上下文中没有对应的变量,则输出空字符串""
$!{变量名.属性},若上下文中没有对应的变量,则输出空字符串""
5.4.3 方法引用
$变量名.方法([入参1[,入参2]*]?),常规写法
${变量名.方法([入参1[,入参2]*]?)},正规写法
$!变量名.方法([入参1[,入参2]*]?),常规写法
$!{变量名.方法([入参1[,入参2]*]?)},正规写法
5.5 指令
#set
作用:在页面中声明变量
语法:#set($变量=值)
#set($number=10) #set($str="hello world $number") ${str}
#if/#elseif/#else
作用:进行逻辑判断
语法:
#if(判断条件) …… #elseif(判断条件) …… #else …… #end
具体实例
#set($language="java") #if($language.equals("java")) java开发工程师 #elseif($language.equals("php")) php开发工程师 #else 开发工程师 #end
#foreach($item in items)
作用:遍历循环数组或者集合
#foreach($item in $items) …… [break] #end
$items:需要遍历的对象或者集合,如果items的类型为map集合,那么遍历的是map的value
$item:变量名称,代表遍历的每一项
#break:退出循环
内置属性:$foreach.index:获取遍历的索引,从0开始,$foreach.count:获取遍历的次数,从1开始
#foreach($item in $hobbies) $item #break #end
#evaluate
作用:动态计算,动态计算可以让我们在字符串中使用变量
语法:#evaluate("计算语句")
可以将服务端的代码直接解析
#include
作用:引入外部资源,引入的资源不会被引擎解析
语法:#include(resources)
resources可以为单引号或者双引号的字符串,也可以为$变量,内容为外部资源路径
注意:路径如果为相对路径,则以引擎配置的文件加载器加载路径作为参考
#parse
作用:引入外部资源,引入的资源将会被引擎解析
语法:#parse(resources)
注意:路径如果为相对路径,则以引擎配置的文件加载器加载路径作为参考
#define
作用:定义重用模块
语法:
#define($模块名称)
模块内容
#end
使用的时候直接 $模块名称
宏指令
作用:定义重用模块(可带参数)
语法:
定义语法:
#macro(宏名 [$arg]?)
……
#end
调用语法:
#宏名([$arg]?)
实例:
#macro(table $hobbies)
#foreach($item in $hobbies)
$item
#end
#end
#table($hobbies)
6.生成代码
6.1 定义模板
创建controller模板 Controller.java.vm
package ${package}.controller; import ${package}.entity.${className}; import ${package}.entity.ResultData; import ${package}.service.${className}Service; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; @RestController @RequestMapping("/${classname}") public class ${className}Controller { @Resource private ${className}Service ${classname}Service; /** * 查询列表 * * @param name * @param addr * @return */ @GetMapping("/list") public ResultData getList(String name, String addr, Integer page, Integer limit, HttpServletRequest r ) { return ${classname}Service.getList(name, addr, page, limit); } /** * 根据电影名称查询影院信息 * * @param name * @return */ @GetMapping("/index") public ResultData getIndexList(String name) { List<${className}> list = ${classname}Service.getIndexList(name); return new ResultData(true, "查询成功", list); } /** * 修改信息 * * @param ${classname} * @return */ @PostMapping("/edit") public ResultData updateInfo(@RequestBody ${className} ${classname}) { int count = ${classname}Service.updateInfo(${classname}); if (count == 0) { return new ResultData(false, "操作失败"); } else { return new ResultData(true, "操作成功"); } } /** * 添加信息 * * @param pojo * @return */ @PostMapping("/add") public ResultData addInfo(@RequestBody ${className} ${classname}) { int count = ${classname}Service.addInfo(${classname}); if (count == 0) { return new ResultData(false, "操作失败"); } else { return new ResultData(true, "操作成功"); } } /** * 根据id删除信息 * * @param ids * @return */ @PostMapping("/del") public ResultData deleteById(@RequestParam("ids") List<Integer> ids) { ${classname}Service.deleteById(ids); return new ResultData(true, "操作成功"); } }
创建service模板 Service.java.vm
package ${package}.service; import ${package}.entity.${className}; import ${package}.entity.ResultData; import java.util.List; public interface ${className}Service { ResultData getList(String name, String addr, Integer page, Integer limit); int updateInfo(${className} ${classname}); int addInfo(${className} ${classname}); void deleteById(List<Integer> ids); List<${className}> getIndexList(String name); }
创建serviceImpl模板 ServiceImpl.java.vm
package ${package}.service.impl; import cn.hutool.core.util.StrUtil; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import ${package}.dao.${className}Dao; import ${package}.entity.${className}; import ${package}.entity.ResultData; import ${package}.service.${className}Service; import org.springframework.stereotype.Service; import tk.mybatis.mapper.entity.Example; import javax.annotation.Resource; import java.util.List; @Service public class ${className}ServiceImpl implements ${className}Service { @Resource private ${className}Dao ${classname}Dao; @Override public ResultData getList(String name, String addr, Integer page, Integer limit) { if (page != null && limit != null) { PageHelper.startPage(page, limit); } Example example = new Example(${className}.class); Example.Criteria criteria = example.createCriteria(); if (StrUtil.isNotBlank(name)) { criteria.andLike("name", "%" + name + "%"); } if (StrUtil.isNotBlank(addr)) { criteria.andLike("addr", "%" + addr + "%"); } List<${className}> list = ${classname}Dao.selectByExample(example); // 是否分页查询 if (page != null && limit != null) { PageInfo<${className}> pageInfo = new PageInfo(list); return new ResultData(true, "查询成功", pageInfo.getList(), pageInfo.getTotal()); } else { return new ResultData(true, "查询成功", list); } } @Override public int updateInfo(${className} ${classname}) { return ${classname}Dao.updateByPrimaryKeySelective(${classname}); } @Override public int addInfo(${className} ${classname}) { return ${classname}Dao.insertSelective(${classname}); } @Override public void deleteById(List<Integer> ids) { if (ids != null && ids.size() > 0) { ids.stream().forEach(item -> ${classname}Dao.deleteByPrimaryKey(item)); } } @Override public List<${className}> getIndexList(String name) { return ${classname}Dao.getIndexList(name); } }
创建dao模板 Dao.java.vm
package ${package}.dao; import ${package}.entity.${className}; import tk.mybatis.mapper.common.Mapper; import java.util.List; public interface ${className}Dao extends Mapper<${className}> { List<${className}> getIndexList(String name); }
6.2 编写工具类
编写生成Java文件工具类
package com.zxh.movieserver.util; 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.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * @Author Eric * @Date 5/26/2021 5:12 PM */ public class GenUtils { /** * 生成Java代码 * @param data 生成到模板中的数据 * @param templates 模板名称 * @param */ public static void generatorCode(Map<String,Object> data, List<String> templates, File file){ // 1.设置velocity的资源加载器 Properties properties=new Properties(); properties.put("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); // 2.初始化velocity引擎 Velocity.init(properties); // 3.创建velocity容器 VelocityContext context=new VelocityContext(data); // 4.加载模板文件 //遍历模板列表,获取每一个模板 for (String template : templates) { Template tpl = Velocity.getTemplate(template, "utf-8"); // StringWriter sw=new StringWriter(); // // 5.合并数据到模板 // tpl.merge(context,sw); String fileName = genFileName(template, data.get("className").toString(), data.get("package").toString()); try { FileWriter fileWriter=new FileWriter(file+File.separator+fileName); tpl.merge(context,fileWriter); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } // try { // zip.putNextEntry(new ZipEntry(fileName)); // IOUtils.write(sw.toString(),zip,"UTF-8"); // IOUtils.closeQuietly(sw); // zip.flush(); // zip.closeEntry(); // } catch (IOException e) { // e.printStackTrace(); // } } } /** * 根据模板名称,类名称,包名称拼接一个完整的文件路径和名称 * @param template 模板名称 * @param className 类名称 * @param packageName 包名称 * @return */ public static String genFileName(String template,String className,String packageName){ String packagePath="main"+ File.separator+"java"+File.separator; if(StringUtils.isNotEmpty(packageName)){ packagePath+=packageName.replace(".",File.separator)+File.separator; } if(template.contains("Controller.java.vm")){ return packagePath+"controller"+File.separator+className+"Controller.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("Dao.java.vm")){ return packagePath+"dao"+File.separator+className+"Dao.java"; } return null; } }
6.3 单元测试
运行测试类就可以生成相关代码
package com.zxh.movieserver; import com.zxh.movieserver.util.GenUtils; import org.junit.jupiter.api.Test; import java.awt.*; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipOutputStream; /** * @Author Eric * @Date 5/26/2021 5:52 PM */ public class Test4 { @Test public void test1() throws FileNotFoundException { Map<String,Object> map=new HashMap<>(); map.put("package","com.zxh.movieserver"); map.put("className","Account"); map.put("classname","account"); //构建模板列表 List<String> list=new ArrayList<>(); list.add("templates/Controller.java.vm"); list.add("templates/Service.java.vm"); list.add("templates/ServiceImpl.java.vm"); list.add("templates/Dao.java.vm"); File file=new File("D:\idea_code\cat-movie-main\cat-movie-main\movie-server\src\"); // FileOutputStream fileOutputStream=new FileOutputStream(file); // ZipOutputStream zipOutputStream=new ZipOutputStream(fileOutputStream); GenUtils.generatorCode(map,list,file); } }