Руководство по развитию скорости

FreeMarker
Руководство по развитию скорости

Введение

Velocity Бесплатный шаблонизатор Java с долгой историей. Официальный сайт:velocity.apache.org/

Velocity — это механизм шаблонов на основе Java, простой, но мощный инструмент разработки, который позволяет легко создавать и отображать документы для форматирования и отображения данных.

*.vm : файлы шаблонов скорости

VTL: язык шаблонов скорости

При использовании механизма шаблонов Velocity необходимо сосредоточиться на двух аспектах:Шаблон скоростиа такжеВызов кода Java.

Шаблон скорости отVTLа такжеобъект контекста движкасоставляют;

Вызывающая часть Java-кода отвечает заДвигатель начальной скорости,Создайте объект контекста движка,Загрузить шаблон скоростиа такжеНачать рендеринг шаблона.

Связь между шаблоном Velocity и вызывающей частью кода Java:объект контекста движка.

Velocity был перенесен на разные платформы, такие как NVelocity из .Net и Velocity.js из js. Хотя каждая платформа имеет небольшие различия в использовании и реализации, большая часть синтаксиса и реализация ядра движка одинаковы, поэтому стоимость обучения уменьшается О много.

Требования к версии

Для Velocity2.1 или выше требуется jdk1.8 или выше


Velocity2.2 зависимости maven

<dependency>  
<groupId>org.apache.velocity</groupId>  
<artifactId>velocity-engine-core</artifactId>  
<version>2.2</version>
</dependency>
​<dependency>  
<groupId>org.apache.velocity.tools</groupId>  
<artifactId>velocity-tools-generic</artifactId>  
<version>3.0</version>
</dependency>​
<dependency>  
<groupId>org.apache.velocity.tools</groupId>  
<artifactId>velocity-tools-view</artifactId>  
<version>3.0</version>
</dependency>​
<dependency>  
<groupId>org.apache.velocity.tools</groupId> 
 <artifactId>velocity-tools-view-jsp</artifactId> 
 <version>3.0</version>
</dependency>

Официальный адрес загрузки всех остальных версий:archive.apache.org/Day 3/VELO Times…

Для Velocity1.7 требуется jdk1.4 или выше. Адрес официального сайта:velocity.apache.org/engine/1.7/

1.7 адрес документа API:tool.OSCHINA.net/API doc S/API…

Быстрый старт

Example2.java

import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;

public class Example2
{
    public static void main( String args[] )
    {
        /* 首先,初始化运行时引擎,使用默认的配置 */

        Velocity.init();

        /* 创建Context对象,然后把数据放进去 */

        VelocityContext context = new VelocityContext();

        context.put("name", "Velocity");
        context.put("project", "Jakarta");

        /* 渲染模板 */

        StringWriter w = new StringWriter();

        Velocity.mergeTemplate("testtemplate.vm", context, w );
        System.out.println(" template : " + w );

        /* 渲染字符串 */

        String s = "We are using $project $name to render this.";
        w = new StringWriter();
        Velocity.evaluate( context, w, "mystring", s );
        System.out.println(" string : " + w );
    }
}

testtemplate.vm

Hi! This $name from the $project project.

Суммировать

В первую очередь нужно еще создать контекст и поместить нужные данные.

затем объединить содержимое

Кодирование Java для Velocity

Следующее воспроизводится из:блог woowowoo.cn on.com/FS Джон паникует…Толстяк Джон

Шаблоны взаимодействуют с хост-средой

Шаблон относится к шаблону Velocity, написанному на VTL, а среда хоста относится к вызывающей части кода Java. Связующим звеном между двумя сообщениями является объект контекста механизма (экземпляр VelocityContext), ниже приведены часто используемые методы экземпляра VelocityContext.

// 构造函数,入参为上下文的键值对集
VelocityContext(Map context)
// 添加上下文的键值对
Object put(String key, Object value)
// 从上下文获取指定键的值
Object get(String key)
// 检查上下文中是否存在指定的键值对
boolean containsKey(Object key)
// 获取所有键
Object[] getKeys()
// 移除指定键
Object remove(Object key)
// 获取上下文链中邻近的上下文对象
Context getChainedContext()

Хост-среда передает значения в шаблон

// 1. 通过构造函数传值
HashMap<String, String> baseCtx = new HashMap<String, String>();
baseCtx.put("version", "1");
VelocityContext ctx = new VelocityContext(baseCtx);

// 2. 通过put传值
ctx.put("author", "fsjohnhuang");

Обратите внимание, что тип данных значения в паре ключ-значение

Целое, длинноеТипы в штучной упаковке для простых типов данных, таких как

Тип строки;

Подкласс объекта;

Тип массива Object[], поскольку Velocity 1.6 обрабатывает типы массивов как типы java.util.List, поэтому в шаблоне можно вызывать переменные методы size() , get(intindex) и isEmpty();

подкласс java.util.Collection;

подкласс java.util.Map;

объект java.util.Iterator;

java.util. Объект перечисления.

Кроме того, мы также можем назначить статический класс объекту контекста, например статический класс java.lang.Math.

ctx.put("Math", java.lang.Math.class);

Шаблоны передают значения в хост-среду

1. Пример связи 1 — получить переменные через объект контекста движка

Файл шаблона frm.vm

#set($action="./submit")
<form action="$action">
  .........
</form>

Раздел кода Java

VelocityContext ctx = new VelocityContext();
VelocityEngine ve = new VelocityEngine();
StringWriter sw = new StringWriter();
ve.mergeTemplate("frm.vm", ctx, sw);
String actionStr = ctx.get("action");
System.out.println(actionStr); // 显示./submit

2. Пример связи 2 — изменение значений переменных и свойств с помощью побочных эффектов

Файл шаблона change.vm

$people.put("john", "john huang")
#set($version = $version + 1)

Раздел кода Java

VelocityContext ctx = new VelocityContext();
ctx.put("version", 1);
HashMap<String, String> people = new HashMap<String,String>();
ctx.put("people", people);
VelocityEngine ve = new VelocityEngine();
StringWriter sw = new StringWriter();
ve.mergeTemplate("change.vm", ctx, sw);
System.out.println(ctx.get("version")); // 显示2
System.out.println(people.get("john")); //显示john huang

В приведенном выше примере показано, что при работе с экземпляром ссылочного типа в шаблоне результат операции повлияет на сам экземпляр ссылочного типа, поэтому действовать следует с осторожностью.

цепочка контекста движка

Также известная как цепочка контейнеров, в настоящее время наиболее часто используется для обеспечения иерархического доступа к данным и набора инструментов.

VelocityContext context1 = new VelocityContext();

context1.put("name","Velocity");
context1.put("project", "Jakarta");
context1.put("duplicate", "I am in context1");

VelocityContext context2 = new VelocityContext( context1 );

context2.put("lang", "Java" );
context2.put("duplicate", "I am in context2");

template.merge( context2, writer );

Так называемая цепочка контекста механизма предназначена для назначения исходного объекта контекста вновь созданному объекту контекста, чтобы добиться повторного использования пар ключ-значение в контексте. Конкретный код выглядит следующим образом:

VelocityContext ctx1 = new VelocityContext();
ctx1.put("name", "fsjohuang");
ctx1.put("version", 1);
VelocityContext ctx2 = new VelocityContext(ctx1);
ctx2.put("version", 2);

System.out.println(ctx2.get("name")); // 显示fsjohnhuang
System.out.println(ctx2.get("version")); // 显示2

То есть, когда текущий объект контекста не имеет пары ключ-значение, запрашивается, имеет ли объект цепочки контекста пару ключ-значение, и возвращается, если есть, и продолжает поиск других объектов контекста в цепочке до тех пор, пока найдена пара ключ-значение или пройдены все цепочки в объекте контекста.

Но помимо методов put и get экземпляры VelocityContext также имеют методы remove, getKeys, containsKey, как они себя ведут? Давайте посмотрим на исходный код ниже!

Часть кода Java, задействованная на официальном сайте

пользовательские свойства

/opt/templates

...

import java.util.Properties;
 ...

public static void main( String args[] )
{
    /* 首先,我们还是初始化运行时引擎  */

    Properties p = new Properties();
    p.setProperty("file.resource.loader.path", "/opt/templates");
    Velocity.init( p );

 ...

Хотя Velocity позволяет создавать собственные классы контейнеров для удовлетворения конкретных потребностей и технологий (например, контейнер с прямым доступом к серверу LDAP), базовый класс реализации под названием VelocityContext был предоставлен вам как часть дистрибутива.

VelocityContext подходит для всех общих нужд, поэтому мы настоятельно рекомендуем вам использовать VelocityContext в качестве контейнера. Только в особых случаях и для расширенных приложений вам необходимо расширять или создавать собственную реализацию контейнера.

поддержка объектов обхода for и foreach()

Velocity поддерживает несколько типов коллекций с помощью оператора foreach() в VTL:

Object []

Массив простых объектов Если класс предоставляет интерфейс итератора, Velocity автоматически перенесет ваш массив

Теперь Velocity позволяет разработчикам шаблонов рассматривать массивы как связанные списки фиксированной длины (это было в Velocity 1.6).

java.util.Collection

Velocity возвращает итератор для использования в цикле через метод iterator().

java.util.Map

Velocity возвращает интерфейс Collection через метод values() интерфейса, для которого вызывается метод iterator() для извлечения итератора для цикла.

java.util.Iterator

В настоящее время поддерживается только временно, итераторы не могут быть сброшены

Если неинициализированный итератор помещен в контейнер и используется в нескольких операторах foreach(), если первый foreach() завершится ошибкой, все последующие будут заблокированы, поскольку итератор не будет перезапущен.

java.util.Enumeration

то же, что java.util.Iterator

Для Iterator и Enumeration рекомендуется помещать их в контейнер только в крайнем случае, а также следует максимально позволить Velocity найти подходящий и повторно используемый интерфейс итерации.

Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");

context.put("words", v.iterator() );//不推荐
context.put("words", v );

Поддержка статических классов

context.put("Math", Math.class);

Таким образом, вы можете вызвать любой общедоступный статический метод в java.lang.Math со ссылкой $Math в шаблоне.

Такой класс, как java.lang.Math, не предоставляет общедоступных конструкторов, но содержит полезные статические методы.

Сервлет использует скорость

Настройте скорость в web.xml

speed.apache.org/tools/… но даже…

<!-- Define Velocity template handler -->
<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>
    org.apache.velocity.tools.view.VelocityViewServlet
  </servlet-class>

  <!-- 
    Unless you plan to put your tools.xml and velocity.properties
    under different folders or give them different names, then these
    two init-params are unnecessary.  The
    VelocityViewServlet will automatically look for these files in
    the following locations.
    -->
  <init-param>
    <param-name>org.apache.velocity.toolbox</param-name>
    <param-value>/WEB-INF/tools.xml</param-value>
  </init-param>

  <init-param>
    <param-name>org.apache.velocity.properties</param-name>
    <param-value>/WEB-INF/velocity.properties</param-value>
  </init-param>
</servlet>

<!-- Map *.vm files to Velocity -->
<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.vm</url-pattern>
</servlet-mapping>

tools.xml подобен определению набора инструментов со многими инструментами, такими как «гаечный ключ».

Конкретный пример: попросите нашего друга Джона взять наш «гаечный ключ» из настоящего ящика с инструментами. Джону просто нужно знать, какой ключ нам нужен. Ему не нужно знать, что делает ключ или что мы собираемся делать.

Velocity Toolbox работает так же, как и в приведенном выше примере, нам нужно только указать необходимые инструменты, а затем Velocity Engine может использовать любые общедоступные методы, определенные в Toolbox Tool.xml в шаблоне vm, для обработки остальных.

PipeWrench.java

public class PipeWrench {
    public String getSize() {
        return "Large Pipe Wrench!";
    }
}

tools.xml

<?xml version="1.0"?>
<tools>
  <toolbox scope="application">
    <tool key="wrench" class="PipeWrench"/>
  </toolbox>
</tools>

В шаблоне .vm вы можете использовать:

$wrench.size.

Шаблон виртуальной машины

Официальное руководство по ВТЛ:

скорость.apache.org/engine/2.2/…

VTL: язык шаблонов скорости

Следующее воспроизводится из:блог woowowoo.cn on.com/FS Джон паникует…Толстяк Джон

Примечания

1. Строковые комментарии

## 行注释内容

2. Блокировать комментарии

#*块注释内容1块注释内容2*#

3. Примечания к документации

#**文档注释内容1文档注释内容2*#

вытоптанная яма

Ни комментарии блока, ни комментарии документа не выводятся в окончательный результат, но вызывают появление окончательного результата.пустая строка. Это не так со строчными комментариями.

прямой вывод

То есть контент, который не будет анализироваться движком.

#[[直接输出的内容1直接输出的内容2]]#

Цитировать

Оператор ссылки должен работать со свойствами в объекте контекста механизма.

С точки зрения грамматики она делится на обычную грамматику ($property) и обычную грамматику (${property}).

В приведенных выше двух способах записи в обычном режиме, когда в объекте контекста движка нет соответствующего свойства, окончательный результат будет напрямую выводить $property или ${property}, если нет, его нужно переписать как $!property и $!{свойство} .

1. Переменные (то есть свойства объекта контекста движка)

$变量名, 常规写法,若上下文中没有对应的变量,则输入字符串"$变量名"
${变量名}, 常规写法,若上下文中没有对应的变量,则输入字符串"${变量名}" 
$!变量名, 常规写法,若上下文中没有对应的变量,则输入空字符串"" 
$!{变量名}, 常规写法,若上下文中没有对应的变量,则输入空字符串""

Правила именования переменных:

Состоит из букв, знаков подчеркивания (_), дефисов (-), цифр и начинается с буквы.

Тип данных переменной:

Целое, длинноеТипы в штучной упаковке для простых типов данных, таких как

Тип строки;

Подкласс объекта;

Тип массива Object[], поскольку Velocity 1.6 обрабатывает типы массивов как типы java.util.List, поэтому в шаблоне можно вызывать переменные методы size() , get(int index) и isEmpty() ;

подкласс java.util.Collection;

подкласс java.util.Map;

объект java.util.Iterator;

java.util. Объект перечисления.

2. Свойства (то есть свойства свойств объекта контекста движка)

$变量名.属性, 常规写法
${变量名.属性}, 正规写法
$!变量名.属性, 常规写法
$!{变量名.属性}, 正规写法

Правила поиска атрибутов:

В Velocity используется очень гибкий способ поиска свойств переменных:

// Если есть ссылка на $var.prop, Velocity преобразует реквизит, а затем попытается вызвать объект $var // Порядок морфинга и попытки следующий

  1. $var.getprop()

  2. $var.getProp()

  3. $var.get("prop")

  4. $var.isProp()

// Для $var.Prop тогда следующее

  1. $var.getProp()

  2. $var.getprop()

  3. $var.get("Prop")

  4. $var.isProp()

Поэтому при получении значения ключа объекта java.util.Map его можно сократить до $map.key , и Velocity автоматически преобразует его в $map.get("key") для поиска!

3. Метод (то есть метод свойств объекта контекста движка)

$变量名.方法([入参1[, 入参2]*]?), 常规写法
${变量名.方法([入参1[, 入参2]*]?)}, 正规写法
$!变量名.方法([入参1[, 入参2]*]?), 常规写法
$!{变量名.方法([入参1[, 入参2]*]?)}, 正规写法

Славный метод на самом деле является операцией вызова метода, фокусвозвращаемое значение,Входа такжепобочный эффектСитуация следующая:

  1. Возвращаемое значение метода будет выведено в конечном результате

  1. тип входных данных

$变量 或 $属性,数据类型参考第一小节;
范围操作符(如:[1..2]或[$arg1..$arg2]),将作为java.util.ArrayList处理
字典字面量(如:{a:"a",b:"b"}),将作为java.util.Map处理
数字字面量(如:1),将自动装箱或拆箱匹配方法定义中的int或Integer入参
  1. побочный эффект

// 若操作如java.util.Map.put方法,则会修改Java代码部分中的Map对象键值对
$map.put("key", "new value")

инструкция

Директивы в основном используются для определения повторно используемых модулей, введения внешних ресурсов и управления потоком процессов. Инструкции начинаются с символа #.

#set: добавить свойства в объект контекста движка или изменить существующие свойства

Формат: #set($variable = value) , конкретный пример выглядит следующим образом:

#set($var1 = $other)
#set($var1.prop1 = $other)
#set($var = 1)
#set($var = true)
#set($var = [1,2])
#set($var = {a:"a", b:"b"})
#set($var = [1..3])
#set($var = [$arg1..$arg2])
#set($var = $var1.method())
#set($var = $arg1 + 1)
#set($var = "hello")
#set($var = "hello $var1") // 双引号可实现字符串拼接(coffeescript也是这样哦!),假设$var1为fsjohnhuang,则$var为hello fsjohnhuang
#set($var = 'hello $var1') // 单引号将不解析其中引用,假设$var1为fsjohnhuang,则$var为hello $var1

Области действия, очевидно, являются глобальными.

#if: условное суждение

Формат:

#if(判断条件)
  .........
#elseif(判断条件)
  .........
#else
  .........
#end

Узнайте об условиях суждения на примерах:

#if($name)   //$name不为false、null和undefined则视为true
$name
#elseif($job == "net") // ==和!=两边的变量将调用其toString(),并对比两者的返回值
Net工程师
#elseif($age <= 16) // 支持<=,>=,>,<逻辑运算符
未成年劳工
#elseif(!$married) // 支持!逻辑运算符
未婚
#elseif($age >= 35 && !$married) // 支持&&,||关系运算符
大龄未婚青年
#end

#форич: цикл

Формат:

#foreach($item in $items)
    ..........
#end

Областью видимости $item является тело цикла #foreach.

Типы данных $items: массивы Object[], [1..2], [1,2,3,4], {a:"a",b:"b"} и с общедоступным Iterator iterator() объект метода следующим образом:

java.util.Collection子类,Velocity会调用其iterator方法获取Iterator对象
java.util.Map子类,Velocity会调用value()获取Collection对象,然后调用调用其iterator方法获取Iterator对象
java.util.Iterator对象,直接将该Iterator对象添加到上下文对象中时,由于Iterator对象为只进不退的操作方式,因此无法被多个#foreach指令遍历
java.util.Enumeration对象,直接将该Enumeration对象添加到上下文对象中时,由于Iterator对象为只进不退的操作方式,因此无法被多个#foreach指令遍历

Встроенное свойство $foreach.count используется для указания номера текущего цикла, начиная с 0. Максимальное количество циклов может быть ограничено директивой элемента конфигурации foreach.maxloops, значение по умолчанию равно -1 (неограниченно).

Пример. Разница между использованием вектора и итератора:

шаблон:

#macro(show)
#foreach($letter in $letters)
$letter
#end
#end
#show()

Java-код:

Vector<String> v = new Vector<String>();
v.add("a");
v.add("b");
VelocityContext ctx = new VelocityContext();
ctx.put("letters",v);
Template t = Velocity.getTemplate("模板路径");
StringWriter sw = new StringWriter();
t.merge(ctx,sw);
System.out.println(sw.toString());
// 结果
// a
// b
// a
// b

ctx.put("letters",v.iterator());
// 结果
// a
//

#break: выйти из цикла

#foreach($item in $items)
#if($item == "over")
#break;
#end
$item
#end

#stop: прервать операцию парсинга шаблона

#set($cmd="stop")
$cmd
#if($cmd == "stop")
#stop
#end
$cmd  // 该语句将不执行

#include импорт внешних ресурсов

(Импортированные ресурсы не анализируются движком)

Формат: #include(ресурс[другойресурс]*)

ресурс, другой ресурс может бытьстрока в одинарных или двойных кавычках, так же может быть$переменная, содержимое — это путь к внешнему ресурсу. Обратите внимание, что если это относительный путь, в качестве системы отсчета используется путь загрузки загрузчика файлов, настроенный движком, а не путь к текущему файлу шаблона.

#parseПредставьте внешние ресурсы

(Импортированные ресурсы будут проанализированы движком)

Формат: #parse(ресурс)

ресурс может бытьстрока в одинарных или двойных кавычках, так же может быть$переменная, содержимое — это путь к внешнему ресурсу. Обратите внимание, что если это относительный путь, в качестве системы отсчета используется путь загрузки загрузчика файлов, настроенный движком, а не путь к текущему файлу шаблона.

Поскольку директива #parse может вызвать проблему бесконечного рекурсивного введения, максимальное количество рекурсивных представлений может быть ограничено элементом конфигурации directive.parse.max.depth, а значение по умолчанию равно 10.

#macro: определить повторно используемый модуль (с параметрами)

Формат определения:

#macro(宏名 [$arg[ $arg]*]?)
   .....
#end

Формат вызова:

#宏名([$arg[ $arg]]?)

Пример 1 -Когда определение и вызов находятся в том же файле шаблона, нет необходимости следовать правилу определить-перед использованием:

#log("What a happy day")
#macro(log $msg)
log message: $msg
#end

Пример 2 -При определении и вызове различных файлов шаблонов вам необходимо следовать правилам сначала определить, а затем использовать:

## 模板文件macro.vm#macro(log $msg)
log message: $msg
#end
## 模板文件main.vm
#parse("macro.vm")
#log("What a happy day")

Анализ принципа: Движок Velocity создаст синтаксическое дерево на основе шаблона и буферизует его перед выполнением.Поэтому, когда определение макроса и вызов находятся в одном файле шаблона, когда макрос вызывается, он был распознан и инициализирован движком (аналог хоста в js).

Если определение и вызов расположены в разных файлах шаблона, поскольку #parse выполняется, когда движок анализирует файл шаблона для импорта внешних ресурсов и инициализации в нем определений макросов, необходимо соблюдать правила определения перед использованием.

Мы можем настроить глобальную библиотеку макросов следующим образом:

Properties props = new Properties();
// velocimacro.library的值为模板文件的路径,多个路径时用逗号分隔
// velocimacro.library的默认值为VM_global_library.vm 
props.setProperty("velocimacro.library", "global_macro1.vm,global_macro2.vm");
VelocityEngine ve = new VelocityEngine(props);

Кроме того, у #macro есть еще один способ передачи параметров — $!bodyContent

#macro(say)
$!bodyContent
#end
#@say()Hello World#end
// 结果为Hello World

#define: определить повторно используемый модуль (без параметров)

#define($log)
hello log!
#end
$log

Его можно рассматривать как слабую версию #macro, вообще не используемую, просто разбираюсь.

#evaluate: динамическая оценка

Пример:

#set($name = "over")
#evalute("#if($name=='over')over#{else}not over#end") // 输出over

Вообще не надо, просто пойми.

Побеги

Экранирование $ и # с помощью \ заставляет синтаксический анализатор не анализировать их.

#set($test='hello')
$test ## 结果:hello
\$test ## 结果:$test
\\$test ## 结果:\hello
\\\$test ## 结果:\$test

$!test ## 结果:hello
$\!test ## 结果:$!test
$\\!test ## 结果:$\!test
$\\\!test ## 结果:$\\!test

Практика шаблонов

Содержание цитируется из:блог woowowoo.cn on.com/FS Джон паникует…толстый мальчик

Результатом примера является создание такой HTML-формы:

<form action="./submit">
<div>
  <label for="title">标题:</label>
  <input type="text" id="title" name="title"/>
</div>
<div>
  <label for="brief">摘要:</label>
  <input type="text" id="brief" name="brief"/>
</div>
<div>
  <label for="sex">性别:</label>
  <select id="sex" name="sex">
    <option value="0">男</option>
    <option value="1">女</option>
  </select>
</div>
<div>
  <label for="job">职业:</label>
  <select id="job" name="job">
    <option value="0">Java工程师</option>
    <option value="1">Net工程师</option>
  </select>
</div>
</form>

Внедрение зависимостей — speed-1.7-dep.jar

Файл шаблона frm.vm

##表单模板
##@author fsjohnhuang
##@version 1.0
## 引入外部模板文件
#parse('macro.vm')
## 主逻辑
<form action="$action">
#foreach($input in $inputs)
#input($input.title $input.id)
#end
#foreach($select in $selects)
#select($select.title $select.id $select.items)
#end
</form>

Файл шаблона macro.vm

## 生成input表单元素区域的宏
#macro(input $title $id)
<div>
  <label for="$id">$title</label>
  <input type="text" id="$id" name="$id"/>
</div>
#end
## 生成select表单元素区域的宏
#macro(select $title $id $items)
<div>
  <label for="$id">$title</label>
  <select id="$id" name="$id">
## VTL指令紧贴左侧才能确保结果的排版正常(不会有多余空格)
#foreach($key in $items.keySet())
    <option value="$key">$items.get($key)</option>
#end
  </select>
</div>
#end

Java-код

public static void main(String[] args) {
        // 初始化模板引擎
        Properties props = new Properties();
        props.put("file.resource.loader.path", ".\\vm");
        VelocityEngine ve = new VelocityEngine(props);
        // 配置引擎上下文对象
        VelocityContext ctx = new VelocityContext();
        ctx.put("action", "./submit");
        ArrayList<HashMap<String, String>> inputs = new ArrayList<HashMap<String,String>>();
        HashMap<String, String> input1 = new HashMap<String, String>();
        input1.put("id", "title");
        input1.put("title", "标题:");
        inputs.add(input1);
        HashMap<String, String> input2 = new HashMap<String, String>();
        input2.put("id", "brief");
        input2.put("title", "摘要:");
        inputs.add(input2);
        ctx.put("inputs", inputs);
        ArrayList<HashMap<String, Object>> selects = new ArrayList<HashMap<String,Object>>();
        HashMap<String, Object> select1 = new HashMap<String, Object>();
        selects.add(select1);
        select1.put("id", "sex");
        select1.put("title", "性别:");
        HashMap<Integer, String> kv1 = new HashMap<Integer, String>();
        kv1.put(0, "男");
        kv1.put(1, "女");
        select1.put("items", kv1);
        HashMap<String, Object> select2 = new HashMap<String, Object>();
        selects.add(select2);
        select2.put("id", "job");
        select2.put("title", "职业:");
        HashMap<Integer, String> kv2 = new HashMap<Integer, String>();
        kv2.put(0, "Java工程师");
        kv2.put(1, "Net工程师");
        select2.put("items", kv2);
        ctx.put("selects", selects);
        // 加载模板文件
        Template t = ve.getTemplate("test.vm");
        StringWriter sw = new StringWriter();
        // 渲染模板
        t.merge(ctx, sw);
        System.out.print(sw.toString());
    }

Справочная статья

Оригинальный адрес руководства по разработке:

speed.apache.org/engine/… в канун…

Оригинальный адрес руководства пользователя:

speed.apache.org/engine/… в канун…

Адрес руководства по разработке китайского перевода:

если eve.com/velocity — так…

Толстяк Джон отлично веб-адрес:

блог woowowoo.cn on.com/FS Джон паникует…