jetbrick-template-2.x 已经发布,新版文档请看这里:http://subchen.github.io/jetbrick-template/2x/

§全局配置选项

名称 说明 默认值
import.packages 默认导入的 java 包
import.classes 默认导入的 java 类
import.variables 默认定义的 java 变量类型
global.variables 全局变量工厂类
import.methods 默认导入的扩展方法
import.functions 默认导入的扩展函数
import.tags 默认导入的自定义标签 tags
import.autoscan 是否自动扫描自定义扩展 Class false
import.autoscan.packages 在指定的包中进行自动扫描
input.encoding 模板源文件的编码格式 utf-8
output.encoding 模板输出编码格式 utf-8
syntax.safecall 是否默认启用全局的安全调用 false
template.loader 模板资源载入Class jetbrick.template.resource.loader.FileSystemResourceLoader
template.path 模板资源的根目录 当前目录
template.suffix 默认模板文件扩展名 .jetx
template.reloadable 是否允许热加载 false
compile.tool 编译器 jetbrick.template.compiler.JdtCompiler
compile.strategy 编译策略 always
compile.debug 是否允许输出 debug 信息 false
compile.path 默认编译输出路径 系统TEMP目录下面的 jetx 目录
security.manager 安全管理器实现类
security.manager.file 安全管理器黑白名单文件
security.manager.namelist 安全管理器黑白名单列表
trim.directive.line 是否要删除指令行两边的空白 true
trim.directive.comments 是否支持指令两边增加注释对 false
trim.directive.comments.prefix 指令注释的开始部分 <!--
trim.directive.comments.suffix 指令注释的结束部分 -->

注意

  1. 所有配置选项都必须在 JetEngine 初始化的时候指定,不允许动态修改。
  2. 所有配置选项都支持变量啦,具体参考 template.path 或者 compile.path 中的例子。

§1. 全局定义(包/类/变量)

在模板中,如果要用到一些其他的 Class, 那么可以先 import 进来,这样就可以在模板中使用短名字,比如 Date 而不是 java.util.Date

§1.1 import.packages

用来配置包名,会自动导入包下面的所有类。允许配置多个包名,用逗号分隔。

支持三种方式,如下:
1. jetbrick.schema.app.model
2. jetbrick.schema.app.methods.*
3. jetbrick.schema.**

其中 12 是一样的,会自动导入包下面的所有 Class 文件,但是不包含子包。
而方式 3 会自动将子包下面的 Class 也一起导入进来。

示例如下:

import.packages = jetbrick.schema.app.model, jetbrick.schema.app.methods.*, jetbrick.schema.**

注意jetbrick-template 会自动引入 java.lang.*java.util.*

§1.2 import.classes

用来配置单个类名,优先级比 import.packages 高。允许配置多个类名,用逗号分隔。

示例如下:

import.classes = java.io.File, java.util.List

§1.3 import.variables

在一个 webapp 中,我们希望每个模板都自动引入一些变量,比如 HttpServletRequest request,那么我们就可以在这里定义。

允许配置多个变量定义,用逗号分隔。示例如下:

import.variables = HttpServletRequest request
import.variables = jetbrick.orm.Pagelist pagelist, List<Entity> entites

变量的类型可以使用泛型定义,并且会自动在 import.packagesimport.classes 里面查找 Class。

注意:全局定义的变量如果在模板中被重新定义成其他类型(#define, #set),则以模板定义优先。

§1.4 global.variables

该配置主要用来设置用户自定义的全局变量。如下:

global.variables = webapp.GlobalVariables

然后用户的实现 webapp.GlobalVariables 如下:

public class GlobalVariables implements JetGlobalVariables  {
    @Override
    public Object get(JetContext context, String name) {
        if ("copyright".equals(name)) {
            return "copyright@2000-2010";
        } else if ("today".equals(name)) {
            return new Date();           
        } else if ("contextPath".equals(name)) {
            HttpServletRequest request = (HttpServletRequest) context.get(JetWebContext.REQUEST);
            return request.getContextPath();
        }
        return null;
    }
}

注意: 用户提供的全局变量,还需要在 import.variables 中声明变量的类型。

import.variables = String copyright, Date today, String contextPath

然后就可以在模板中使用了

${copyright}
${today.format("yyyy-MM-dd")}
${contextPath}

§2. 扩展方法/函数/标签

§2.1 import.methods

我们知道一个 Java Class 的 所有 methods 都是定义在同一个 class 文件中的,不能在其他地方进行动态扩展。但是一些其他动态语言却支持在 Class 外部为这个 Class 增加一些方法。比如:

jetbrick-template 也在这里带给大家强大的动态方法扩展机制。
具体参考: jetbrick-template 动态方法扩展

这里就是把实现了动态扩展的 Method Class 注册到 JetEngine 中。允许配置多个 Class 定义,用逗号分隔。示例如下:

import.methods = StringMethods, app.project.methods.UserAuthMethods

定义的类名会自动在 import.packages 里面查找 Class。

jetbrick-template 默认会注册 jetbrick.template.runtime.JetMethods
具体参考:默认的方法扩展 Methods

§2.2 import.functions

import.methods 类似,我们还支持在模板中使用函数。

允许配置多个 Function Class 定义,用逗号分隔。示例如下:

import.functions = app.project.methods.UserAuthFunctions

jetbrick-template 默认会注册 jetbrick.template.runtime.JetFunctions
具体参考:默认的函数扩展 Functions

§2.3 import.tags

我们支持在模板中自定义标签 #tag。

允许配置多个 Tag Class 定义,用逗号分隔。示例如下:

import.tags = app.project.tags.UserTags

jetbrick-template 默认会注册 jetbrick.template.runtime.JetTags
具体参考:默认的自定义标签 Tags

§2.4 import.autoscan

是否自动扫描用户自定义的扩展 Class,扫描的内容是: 扩展方法,扩展函数,自定义标签

默认 false,不启用。

§2.5 import.autoscan.packages

在指定的包下面进行自动扫描,如果为空,那么扫描整个 classpath。支持定义多个包。

import.autoscan = true
import.autoscan.packages = app.methods, app.functions, app.tags

注意

  1. 扫描整个 classpath 需要花费一定的时间(大约每秒10000个类),建议配置 import.autoscan.packages 以加快速度。
  2. 由于不会对扫描的 class 加载到 jvm 中,所以不会产生 OOM。

更多详细内容请参考: 如何让自动扫描发现用户自定义的扩展方法/函数/标签 Class

§3. 模板路径和编码格式

§3.1 input.encoding

模板源文件的编码格式,默认为 utf-8

§3.2 output.encoding

模板输出内容的编码格式,默认为 utf-8

注意:一般在 web 中,output.encoding 应该和 html 页面的 contentType 中的编码,以及 responsecharacterEncoding 完全一致。

§4. 语法选项

§4.1 syntax.safecall

jetbrick-template 支持 4 种方法的安全调用(类似于 Groovy),以避免出现 NullPointerException

  1. 属性调用 bean?.property
  2. 方法调用 bean?.method(...)
  3. 数组访问 array?[index]
  4. Map访问 map?[key]

如果 syntax.safecall = true,那么将会把全局默认的语法变成安全调用语法。
bean.property 将等价于 bean?.property。这样我就可以省略 ? 拉。

默认为 false,不启用。

§4.2 template.loader

如何找到我们自己的模板文件呢?这里就是定义我们要使用的查找类。我们支持下面几种 Class

template.loader = jetbrick.template.resource.loader.FileSystemResourceLoader
template.loader = jetbrick.template.resource.loader.ClasspathResourceLoader
template.loader = jetbrick.template.resource.loader.JarResourceLoader
template.loader = jetbrick.template.web.WebResourceLoader
template.loader = jetbrick.template.resource.loader.MultipathResourceLoader

默认为 jetbrick.template.resource.loader.FileSystemResourceLoader

注意:如果是 Web 集成模式,默认值为 jetbrick.template.web.WebResourceLoader

§4.3 template.path

除了要定义 template.loader,我们还需要定义模板存放的根目录。

默认为系统当前目录:System.getProperty("user.dir")

注意:如果是 web 集成模式,默认为 webapp 的根目录。具体请参考:JetEngine 自动加载方式 中注意事项。

template.loader = jetbrick.template.resource.loader.FileSystemResourceLoader
template.path = /opt/app/templates/
template.loader = jetbrick.template.resource.loader.ClasspathResourceLoader
template.path = /META-INF/templates/
template.loader = jetbrick.template.resource.loader.JarResourceLoader
template.path = /opt/app/templates.jar
template.loader = jetbrick.template.web.WebResourceLoader
template.path = /WEB-INF/templates
template.loader = jetbrick.template.resource.loader.MultipathResourceLoader
template.path = file:/path/to, classpath:/, jar:/path/to/sample.jar, webapp:/WEB-INF/templates

注意:template.path 支持多种路径,由逗号分隔。每个路径由一个前缀开头,代表相应的 ResouceLoader。具体如下:

前缀 代表的 ResourceLoader
file: FileSystemResourceLoader
classpath: ClasspathResourceLoader
jar: JarResourceLoader
webapp: WebResourceLoader
<MyClassLoader>: 用户自定义的 ResourceLoader (完整类名)


现在 template.path 支持变量了,如:

template.path = ${user.dir}/templates
template.path = ${webapp.dir}/WEB-INF/templates

那么我们支持哪些变量呢?其实这些变量都来自于 System.getProperty(name),只要 System 里有的,都支持。
其中 webapp.dir 是个特殊变量,由 Web 集成框架在系统启动的时候,通过 System.setProperty("webapp.dir", servletContext.getRealPath("/")) 设置的。

§4.4 template.suffix

默认的模板文件扩展名 .jetx,主要用于 Web 框架集成中,用于查找和过滤模板用。

§4.5 template.reloadable

在开发模式下面,我们一般需要频繁的修改模板内容来进行调试。那么我们需要打开这个功能来支持模板的热部署。(类似于 JSP

是否需要重新编译和加载模板,取决于模板源文件的最后修改时间。

默认为 false,建议只在开发模式中启用。

§5. 编译选项

jetbrick-template 采用编译成 Java ByteCode 来提高性能。

§5.1 compile.tool

模板编译器的配置,默认使用 Eclipse Java Compiler, 如果不存在,那么将切换到 JDK 自带的编译器 (需要使用 JDK,而不是 JRE)。

目前可选的配置如下:

# 使用 Eclipse Java Compiler (默认值)
compile.tool = jetbrick.template.compiler.JdtCompiler

# 使用 JDK Compiler
compile.tool = jetbrick.template.compiler.JdkCompiler

Eclipse Java Compiler 需要引入第三方 jar (Tomcat 等 WebServer 一般都自带该 jar)

<dependency>
    <groupId>org.eclipse.jdt.core.compiler</groupId>
    <artifactId>ecj</artifactId>
    <version>4.3.1</version>
</dependency>
  1. 对于不支持 javax.tools.JavaCompiler 接口的 BAE (Baidu App Engine), 应该使用该编译器。
  2. 对于出现未知的编译错误的时候,可以尝试切换编译器。

§5.2 compile.strategy

模板从 1.2.0 开始,提供更加灵活的编译策略。由下面 4 中情况

compile.strategy = precompile
compile.strategy = always
compile.strategy = auto
compile.strategy = none

注意:
不管采用什么模式,对于使用 JetEngine.createTemplate(source) 直接由源码创建的模板,仍然需要进行编译。

§5.3 compile.debug

是否在日志中打印输出模板生成的 Java Source 源代码。

默认 false,建议在开发模式中启用。

注意:同时需要 slf4j 的配合才能输出日志。默认已经开启了 INFO 级别的日志。

§5.4 compile.path

在模板编译的时候,会先生成对应的 .java 文件,然后在把 .java 文件编译成 .class 文件。我们生成的这 2 种文件就放在这个目录下面。

在用 Eclipse 进行 debug 的时候,可以 link 这个目录为 sourcepath 来进行 debug。
具体参考:如何调试模板 debug?

默认会在系统TEMP目录 System.getProperty("java.io.tmpdir") 下面新建一个 jetx 目录。如果这个目录非法或者没有写的权限,那么就会抛出 Exception。

注意

  • 如果一个应用中使用多个 JetEngine 实例,请配置不同的 compile.path 防止出现冲突。我们建议用户每次都重定义这个路径。


现在 compile.path 支持变量了,如:

compile.path = ${java.io.tmp}/jetx
compile.path = ${webapp.dir}/WEB-INF/jetx_classes

那么我们支持哪些变量呢?其实这些变量都来自于 System.getProperty(name),只要 System 里有的,都支持。
其中 webapp.dir 是个特殊变量,由 Web 集成框架在系统启动的时候,通过 System.setProperty("webapp.dir", servletContext.getRealPath("/")) 设置的。

§6. 安全管理器

从 1.2.0 开始,模块新增了安全管理器,特别适合于 CMS 软件,允许用户自定义模板的场景。

§6.1 security.manager

配置安全管理器的实现类,默认为空,表示禁用安全管理器。

启用方式(使用默认的安全管理器):

security.manager = jetbrick.template.parser.JetSecurityManagerImpl

用户也可以实现自己的安全管理器,只要实现接口: jetbrick.template.JetSecurityManager 即可。

安全管理器只在对模板进行解析编译的时候进行,运行期不会影响任何性能。

§6.2 security.manager.file

给默认的安全管理器,配置黑白名单,将该名单放在独立的外部文件中。(每行一个名单)

security.manager.file = ${webapp.dir}/WEB-INF/jetx-white-black-list.txt

§6.3 security.manager.namelist

给默认的安全管理器,配置黑白名单,多个名单以逗号分隔。

security.manager.namelist = -java.lang.System.exit \
                            -java.lang.reflect \
                            -java.sql \
                            -javax.tools \
                            -java.io \
                            +java.io.File.getName \
                            +java.io.File.getPath \
                            -sun \

security.manager.filesecurity.manager.namelist 二选一配置即可。

黑白名单的格式如下:

  1. 前缀符号:
* `+` 开头代表白名单
* `-` 开头代表黑名单
* 没有开始符号,则默认为白名单
  1. 名单格式:
* 包名: `pkg`
* 类名名: `pkg.class`
* 方法名: `pkg.class.method`
* 字段名: `pkg.class.field`

实例:

-java.sql                           // 禁止访问 java.sql 下面的任何 Class,包括所有孙子包下面的 Class
-java.lang.System.exit              // 禁止调用 System.exit() 方法
+java.util.Collections.EMPTY_LIST   // 允许访问 Collections.EMPTY_LIST 字段

§7. 注释指令

由于目前的指令一般直接嵌入在 HTML,对于一些使用可视化编辑器的用户来说,可能会造成一些干扰。
模板从 1.0.1 开始增加对指令注释支持,如:<!-- #if (...) -->; 增强对可视化编辑器的友好度。

§7.1 trim.directive.comments

是否开启对注释指令的支持,默认为 false,表示不启用。

§7.2 trim.directive.comments.prefix

设置注释开始格式,默认为 <!--

§7.3 trim.directive.comments.suffix

设置注释开始格式,默认为 -->

注意: 如果开启注释指令的支持,系统并没有强制要求 trim.directive.comments.prefixtrim.directive.comments.suffix 必须配对出现。也就是说如果使用 <!-- #end 也是可以的。当然我们还是建议你配对使用。

范例:

<table>
<!-- #for (User user: userlist) -->
  <tr>
    <td>${user.name}</td>
    <td>${user.email}</td>
  </tr>
<!-- #end -->
</table>

§8. 其他选项

§8.1 trim.directive.line

由于指令之间存在很多的空白内容,而空白内容也会被作为原始文本原封不动的输出,这样会造成很多输出的内容参差不齐。这个就是用来优化输出格式的,对于用模板来进行代码生成时候特别有用。不建议关闭。

模板示例:

#for (int n: [1,2,3])
${n}
#end

禁用后效果:false


1

2

3

启用后的效果:true (默认启用)

1
2
3

§9. 推荐配置

§9.1 开发环境

import.packages = pkg1, pkg2
import.autoscan = true
import.autoscan.packages = pkg1, pgk2

template.path = /path/to/templates/
template.reloadable = true

compile.strategy = always
compile.path = /path/to/temp/
compile.debug = true

§9.2 生产环境

import.packages = pkg1, pkg2
import.autoscan = true
import.autoscan.packages = pkg1, pgk2

template.path = /path/to/templates/
template.reloadable = false

compile.strategy = precompile
compile.path = /path/to/temp/
compile.debug = false