Velocity初入门

Velocity初入门:

前言:

  • Velocity是一个Java的模板引擎, 所以在现如今的一部分项目中还是沿用这类模板, 所以Big-man的朋友也在沿用这部分模板, 也就是需要Big-man进行分析一下。
  • 在现今的软件开发过程中,软件开发人员将更多的精力投入在了重复的相似劳动中。特别是在如今特别流行的MVC架构模式中(2014年左右的中华民族),软件各个层次的功能更加独立,同时代码的相似度也更加高。所以我们需要寻找一种来减少软件开发人员重复劳动的方法,让程序员将更多的精力放在业务逻辑以及其他更加具有创造力的工作上。Velocity这个模板引擎就可以在一定程度上解决这个问题。

Velocity的介绍:

  • Velocity是一个Java的模板引擎, 这个Big-man在前言当中已经提到过;
  • Velocity通过特定的语法, 可以获取到在java语言中定义的对象, 从而实现界面和java代码的真正分离, 这意味着可以使用velocity替代jsp的开发模式了(实际上Big-man的朋友的公司就是采用的这种模式开发的);
  • Velocity这种开发模式是的前端开发人员可以和Java程序员开发人员同步开发一个遵循MVC架构的Web站点, 在实际应用中, velocity还可以应用于很多其他的场景,这里阐述的是它较为重要的一个作用;

Velocity的使用介绍:

  • Velocity是一个基于Java的模板引擎, 其提供了一个Context容器, 在java代码里面Big-man可以往容器中存值, 然后在vm文件中使用特定的语法获取, 这是velocity基本的用法, 其与jsp, fressmarker并称为三大试图展现技术, 相对于jsp而言, velocity对前后端的分离更加彻底: 在vm文件中不允许出现java代码, 而jsp文件中却可以。
  • 作为一个模板引擎, 除了作为前后端分离的MVC展现层, Velocity还有一些其他用途, 比如源代码生成, 自动email转换xml等;

Velocity的基本用法:

  • Velocity是一个基于 Java 的模板引擎框架,提供的模板语言可以使用在Java中定义的对象变量上。
  • VelocityApache 基金会的项目,开发的目标是分离MVC模式中的持久化层业务层。但是在实际应用过程中,Velocity不仅仅被用在了 MVC 的架构中,还可以被用在以下一些场景中。
    • 1、Web应用: 开发者在不使用JSP的情况下, 可以用VelocityHTML具有的动态内容的特性;
    • 2、源代码生成: 可以用Velocity自动生成Java代码、SQL或者PostScript。有很多开源和商业化的软件使用Velocity引擎模板来进行开发。
    • 3、自动Email: 很多软件的用户注册、密码提醒或者报表都是使用Velocity来自动生成的。使用Velocity可以在文本文件里面生成邮件内容, 而不是在Java代码中拼接字符串。
    • 4、转换xml: Velocity提供一个叫Anakiaant任务, 可以读取XML文件并让它能够被Velocity模板读取。一个比较普遍的应用是将xdoc文档转换成带样式的HTML文件。

Velocity的测试:

  • 和学习所有新的语言或者框架的顺序一样,Big-man需要从最初的部署进行开发做起。首先在 Velocity 的官网上下载最新的发布包,之后使用 Eclipse 建立普通的 Java 项目。引入解压包中的 velocity-1.7.jar 和 lib 文件夹下面的 jar 包。这样我们就可以在项目中使用 Velocity 了。
  • 但是Big-man采用的是Maven下写的, 代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jackdan.velocityTest</groupId>
    <artifactId>velocityTestModel</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--velocity-->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-tools</artifactId>
            <version>2.0</version>
        </dependency>
    </dependencies>
</project>
  • 然后再reimport一下就可以了。

书写测试代码:

  • 以上的搭建引入只是一个热身, 接下来就是用Velocity给各位观众打招呼了, 比如:
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.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by JackDan9 on 2017/10/14.
 */
public class HelloVelocity {
    public static void main(String[] args) {
        VelocityEngine ve = new VelocityEngine();
        ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        try {
            ve.init();
        } catch (Exception e) {
            System.out.println("Exception encountered: " + e);
        }
        VelocityContext ctx = new VelocityContext();
        ctx.put("name", "Velocity");
        List list = new ArrayList();
        list.add("1");
        list.add("2");
        ctx.put("list", list);
        StringWriter sw = new StringWriter();
        try {
            Template t = ve.getTemplate("hellovelocity.vm");
            t.merge(ctx, sw);
        } catch (IOException exc) {
            System.out.println("Exception encountered: " + exc);
        } catch (Exception e) {
            System.out.println("Exception encountered: " + e);
        }
        System.out.println(sw.toString());
    }
}
  • HelloVelocity的代码中,首先new了一个VelocityEngine类;
  • VelocityEngine设置了Velocity使用的一些配置, 相当于初始化;
  • 在初始化引擎之后就可以读取hellovelocity.vm这个模板生成的Template这个类;
  • 之后的VelocityContext类是配置Velocity模板读取的内容;
  • 这个context可以存入任意类型的对象或者变量,让template来读取;
  • 这个操作就像是在使用JSP开发时,往request里面放入key-value,让JSP读取一样;
  • 接下来就是写hellovelocity.vm文件了,这个文件实际定义了Velocity的输出内容和格式。hellovelocity.vm的内容如下:
#set($greet = 'hello')
$greet $name
#foreach($i in $list)
    $i
#end
$greet
  • 对应的输出结果如下:
hello Velocity
    1
    2
  • 在输出结果中我们可以看到,$name、$greet都被替换成了在HelloVelocity.java里面定义的变量,在foreach语句里面遍历了list的每一个元素,并打印出来。而$greet则是在页面中使用#set定义的变量。

基本模板语言语法使用:

  • hellovelocity.vm里面可以看到很多以#$符开头的内容(在Python语言中#号是注释符号,但是在这里不是),这些都是Velocity的语法。
  • Velocity所有的关键字都是以#开头的,而所有的变量则是以$开头。
  • Velocity语法类似于JSP中的JSTL,甚至可以定义类似于函数的宏

变量:

  • 和我们所熟知的其他编程语言一样,Velocity也可以在模板文件中有变量的概念。
  • 1、变量定义:
    • #set($name ="velocity")
  • 等号后面的字符串Velocity引擎将重新解析,例如出现以$开始的字符串时,将做变量的替换
    • #set($hello ="hello $name")
  • 上面的这个等式将会给$hello赋值为"hello velocity"

变量的使用:

  • 在模板文件中使用$name或者${name}来使用定义的变量。
  • Big-man在这里推荐推荐使用${name}这种格式,因为在模板中同时可能定义了类似$name$names的两个变量,如果不选用大括号的话,引擎就没有办法正确识别$names 这个变量。
  • 对于一个复杂对象类型的变量,例如$person,可以使用${person.name}(JS中的数据语法就是类似的)来访问personname属性。
  • 值得注意的是,这里的${person.name} 并不是直接访问personname属性,而是访问persongetName()方法(这个就和JS中的语法不一致了, 底部的实现),所以${person.name}${person.getName()}是一样的。

变量赋值:

  • 在第一小点中,定义了一个变量,同时给这个变量赋了值
  • 对于Velocity来说,变量是弱数据类型的,可以在赋了一个String给变量之后再赋一个数字或者数组给它。
  • 可以将以下六种数据类型赋给一个Velocity变量: 变量引用, 字面字符串, 属性引用, 方法引用, 字面数字, 数组列表
#set($foo = $bar)
#set($foo =“hello”)
#set($foo.name = $bar.name)
#set($foo.name = $bar.getName($arg))
#set($foo = 123)
#set($foo = [“foo”,$bar])

循环:

  • 在 Velocity 中循环语句的语法结构如下:
#foreach($element in $list)
 This is $element
 $velocityCount
#end
  • Velocity引擎会将list中的值循环赋给element变量,同时会创建一个$velocityCount的变量作为计数,从1开始,每次循环都会加1

条件语句:

  • 条件语句的语法如下:
#if(condition)
...
#elseif(condition)#else#end

关系操作符:

  • Velocity 引擎提供了ANDORNOT操作符,分别对应&&||!例如:
#if($foo && $bar)
#end

宏:

  • Velocity中的宏可以理解为函数定义。定义的语法如下:
#macro(macroName arg1 arg2 …)
...
#end
  • 调用这个宏的语法是:
  • #macroName(arg1 arg2 …)
  • 这里的参数之间使用空格隔开,下面是定义和使用 Velocity 宏的例子:
#macro(sayHello $name)
hello $name
#end
#sayHello(“velocity”)
  • 输出的结果为hello velocity;

#parse#include:

  • #parse#include 指令的功能都是在外部引用文件,而两者的区别是,#parse 会将引用的内容当成类似于源码文件,会将内容在引入的地方进行解析,#include是将引入文件当成资源文件,会将引入内容原封不动地以文本输出。分别看以下例子:
  • foo.vm文件:
    • #set($name ="velocity")
  • parse.vm
    • #parse("foo.vm")
  • 输出结果为: velocity
    • include.vm:
    • #include("foo.vm")
  • 输出结果为: #set($name =“velocity”)

自动生成代码的例子:

  • 在上个例子中我们可以生成任意的字符串并且打印出来,那为什么我们不能生成一些按照既定格式定义的代码并且写入文件呢。
  • 在这里我们以一个实际的demo来完成这部分内容。相关内容的源码可以参照附件。
  • 这个demo的功能是要实现一个学生和老师的管理,实际上都是单张表的维护。
  • 我们希望能够只定义model层,来生成MVC的所有代码。
  • 在这个demo中,只自动生成actionJSP的内容,因为现在有很多工具都可以帮助我们自动生成这两个包的代码。
  • 首先在eclipse中建立一个Java web工程,在例子中为了方便管理jar包,使用的是maven来建立和管理工程。

目录分析:

目录

  • Java Resource中放的是Java源码以及资源文件;
  • Deployed Resources中放的是 web 相关的文件。
  • Java文件中使用了类似Spring@Component@Autowired的注解来实现IoC,使用 @Action 这样的注解实现 MVC,而在 JSP 中则使用了 JSTL 来输出页面。
  • 在上图所示的目录中,annotationfilterframeworkutil这四个package是作为这个项目框架的,跟业务没有关系,类似于 spring 和 struts 的功能。
  • 在实际的项目中我们当然希望能够一开始就编写一个通用的模板文件,然后一下子生成所有的代码,但是很多时候这样做是不可能的,或者说比较困难。
  • 为了解决这个问题,我们可以在编写Velocity模板文件之前先按照原本的流程编写代码,暂时先忘掉Velocity
  • 编写的代码应该能够在一个功能上完整的调通涉及MVC中所有层次的内容。
  • 在这个例子中,先编写好StudentAction.java文件,以及上图中webapp目录中所示的文件。
  • 在写好以上代码,同时也能顺利运行之后,我们可以参照之前编写的代码来写模板文件。
  • 这里我们来分别看一个Java文件和JSP的例子。

实际代码分析:

  • ActionTemplate.vm:
#parse ("macro.vm")

@Action("${classNameLowCase}Action")
public class ${classNameUpCase}Action extends BaseAction{
 @Autowired
 public ${classNameUpCase}Dao ${classNameLowCase}Dao;
 private List<${classNameUpCase}> ${classNameLowCase}s;
 private ${classNameUpCase} ${classNameLowCase};
#foreach ($attr in ${attrs})
 private ${attr[0]} ${attr[1]};
#end
 public String ${classNameLowCase}List() {
 ${classNameLowCase}s = ${classNameLowCase}Dao.retrieveAll${classNameUpCase}s();
 return "${classNameLowCase}List.jsp";
 }

 ...
}
  • macro.vm文件中定义了一些使用的宏
  • JSP 的改造相对于 Java 文件来说稍微有点复杂,因为 JSP 中使用 JSTLrequest 中的值也是使用${name} 这样的语法,所以想要输出${name} 这样的字符串而不是被模板引擎所替换,则需要使用转义字符,就像这样:\${name}
  • 为了能够让这个文件中的 table 得到复用,我们将这个文件中的表格单独拿出来,使用 #parse 命令来包含。下面是 ListJspTemplate.vmListTableTemplate.vm 的内容:
  • ListJspTemplate.vm:
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <%@ include file="includeJS.jsp" %>
 <script type="text/javascript">
 var pageConfig = {
 "list" : {
 "action" : "${classNameLowCase}Action!${classNameLowCase}List.action"
 }
 ...
 "idName" : "${classNameLowCase}Id"
 };
 </script>
 <script type="text/javascript" src="common.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>${classNameUpCase} List</title>
</head>
<body>
<h1>${classNameUpCase} List</h1>
<div><button id="addButton">Add</button></div>
#parse ("ListTableTemplate.vm")
<div id="modifyDiv"></div>
<div id="addDiv"></div>
</body>
</html>
  • ListTableTemplate.vm:
#parse ("macro.vm")
#set($plus = "status.index+1")
<table border="1" style="width: 100%">
 <thead>
 <tr><th>No.</th>#generateTH($attrs)</tr>
 </thead>
 <tbody>
 <c:forEach var="${classNameLowCase}" items="${${classNameLowCase}s }" varStatus="status" >
 <tr ${classNameLowCase}Id="${${classNameLowCase}.id }">
 <td>${${plus}}</td>#generateTD($classNameLowCase $attrs)<td>
 <button class="modifyButton">Modify</button>
 <button class="deleteButton">Delete</button></td></tr>
 </c:forEach>
 </tbody>
</table>
  • 在定义好所有的模板文件之后,需要做的是读取这些文件,然后根据这些文件将 model 的数据类型以及名称设置到 context 中,最后将解析出来的内容写到相应的目录中去。这些工作我们放在了一个叫做 VelocityGenerator 的类中来做,它的源码如下:
  • TemplateGenerator.java:
public class VelocityGenerator {

 public static void main(String[] args) {
 VelocityEngine ve = new VelocityEngine();
 ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
 ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());

 ve.init();
 Template actionTpt = ve.getTemplate("ActionTemplate.vm");
 Template listJspTpt = ve.getTemplate("ListJspTemplate.vm");
 Template addTpt = ve.getTemplate("AddTemplate.vm");
 Template modifyTpt = ve.getTemplate("ModifyTemplate.vm");
 VelocityContext ctx = new VelocityContext();

 ctx.put("classNameLowCase", "teacher");
 ctx.put("classNameUpCase", "Teacher");
 String[][] attrs = {
 {"Integer","id"},
 {"String","name"},
 {"String","serializeNo"},
 {"String","titile"},
 {"String","subject"}
 };
 ctx.put("attrs", attrs);
 String rootPath = VelocityGenerator.class.getClassLoader().getResource("").getFile() + "../../src/main";
 merge(actionTpt,ctx,rootPath+"/java/com/liuxiang/velocity/action/TeacherAction.java");
 merge(listJspTpt,ctx,rootPath+"/webapp/teacherList.jsp");
 merge(addTpt,ctx,rootPath+"/webapp/teacherAdd.jsp");
 merge(modifyTpt,ctx,rootPath+"/webapp/teacherModify.jsp");
 System.out.println("success...");
 }

 private static void merge(Template template, VelocityContext ctx, String path) {
 PrintWriter writer = null;
 try {
 writer = new PrintWriter(path);
 template.merge(ctx, writer);
 writer.flush();
 } catch (FileNotFoundException e) {
 e.printStackTrace();
 } finally {
 writer.close();
 }
 }
}
  • 在运行以上代码之后,项目文件夹中将会出现与 Teacher 相关的代码文件。
  • 在实际项目中可能不会出现很多这种单张表维护的情况,而且业务逻辑系统架构会更加复杂,编写模板文件就更加不容易。但是无论多复杂的系统,不同的业务逻辑之间一定或多或少会有相似的代码,特别是在 JSPJS 显示端文件中,因为我们在一个系统中要求显示风格操作方式一致的时候就免不了会有相似内容的代码出现。
  • 在总结这些相似性之后我们还是可以使用 Velocity 来帮助我们生成部分内容的代码,而且即使有一些非共性的内容,我们也可以在生成的代码中继续修改。使用 Velocity 的另外一个好处是生成出来的代码更好维护,风格更加统一。

Jackdan9 Thinking

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值