FreeMarker笔记 第四章 其它

4.1 自定义指令

4.1.1 简介

自定义指令可以使用macro指令来定义。Java程序员若不想在模板中实现定义指令,而是在Java语言中实现指令的定义,这时可以使用freemarker.template.TemplateDirectiveModel类来扩展,后边会讲。

4.1.2 基本内容

宏是有一个变量名的模板片段。你可以在模板中使用宏作为自定义指令,这样就能进行重复性的工作。例如,创建一个宏变量来打印大号的”hello Joe!“;

<#macro greet>
    <font size="+2">Hello Joe!</font>
</#macro>

可以在FTL标记中通过@代替#来使用自定义指令。

<@greet></@greet>
4.1.3 参数

我们仅在greet宏中定义一个变量,person;

<#macro greet person>
    <font size="+2">Hello ${person}!</font>
</#macro>

那么可以这样使用宏:

<@greet person="Fred"/>and<@greet person="Batman"/>

可以去掉双引号, <@greet person=Fred/>

多个参数时,可以设置默认值,要不然的话使用的时候必须指定对应的多个参数:

<#macro greet person color="black">
    <font size="+2" color="${color}">Hello ${person}!</font>
</#macro>

那么就可以这样使用了

<@greet person="Fred"/>
实例

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/macro.ftl


    <h2>没有参数</h2>
    <p>
        <#macro greet>
            <font size="+2">Hello Joe!</font>
        </#macro>
        <@greet/>
    </p>
    <h2>有参数</h2>
    <p>
        <#macro greet person>
            <font size="+2">Hello ${person}!</font>
        </#macro>
        <@greet person="Fred"/> and <@greet person="Bob"/>
    </p>
    <h2>多个参数时,可以设定默认值</h2>
    <p>
        <#macro greet person color="black">
            <font size="+2" color="${color}">Hello ${person}!</font>
        </#macro>
        <@greet person="Fred"/> and <@greet person="Bob" color="red"/>
    </p>

输出:

4.1.4 嵌套内容

自定义指令可以嵌套内容,和预定义指令相似:<#if ...>nested content</#if>。比如,下面这个例子中是创建了一个可以为嵌套的内容画出边框;

<#macro border>
    <table border=4 cellspacing=0 cellpadding=4>
        <tr>
            <td>
                <#nested>
            </td>
        </tr>
    </table>
</#macro>

<#nested>标签执行位于开始和结束标签之间的模板代码块,例如:

<@border>The bordered text</@border>

输出:

<table border=4 cellspacing=0 cellpadding=4>
    <tr>
        <td>
            The bordered text
        </td>
    </tr>
</table>

<#nested>指令可以被执行多次;

<#macro do_thrice>
    <#nested>
    <#nested>
    <#nested>
</#macro>

使用时:

<@do_thrice>anything</@do_trice>

输出:

anything
anything
anything

如果不使用nested指令,那么嵌套的内容就会被执行,如果不小心写成这样:

<@greet person="Joe">
    Anything.
</@greet>

输出是:

<font size="+2">Hello Joe!</font>

嵌套的内容被忽略了,因为greet没有使用nested指令;

嵌套内容可以是其他FTL指令,包含其他自定义指令也是可以的。

<@border>
    <ul>
        <@do_thrice>
            <li><@greet person="Joe"></li>
        </@do_thrice>
    </ul>
</@border>

在嵌套的内容中,宏的局部变量是不可见的;

<#macro repeat count>
    <#local y="test">
    <#list 1..count as x>
        ${y} ${count}/${x} : <#nested>
    </#list>
</#macro>
<@repeat count=3>${y!"?"} ${x!"?"} ${count!"?"}</@repeat>

将会输出:

test 3/1: ? ? ?
test 3/2: ? ? ?
test 3/3: ? ? ?

不同的局部变量的设置是为每个宏自己调用的,所以不会导致混乱;

<#macro test foo>${foo} (<#nested>) ${foo}</#macro>
<@test foo="A"><@test foo="B"><@test foo="C"/></@test></@test>

输出:

A (B (C () C) B) A
4.1.5 宏与循环变量

循环变量的名字是已经给定了,变量值的设置由指令本身完成。

<#macro do_thrice>
    <#nested 1>
    <#nested 2>
    <#nested 3>
</#macro>
<p>
    <#--自定义循环变量需要用;代替as-->
    <@do_thrice ; x>
        do_something : ${x}
    </@do_thrice>
</p>

输出:

do_something : 1 do_something : 2 do_something : 3 

例子:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/nested.ftl

<h3>宏与循环变量</h3>
<#macro do_thrice>
    <#nested 1>
    <#nested 2>
    <#nested 3>
</#macro>
<p>
    <#--自定义循环变量需要用;代替as-->
    <@do_thrice ; x>
        do_something : ${x}
    </@do_thrice>
</p>

一个宏可以使用多个循环变量(变量的顺序是很重要的):

<#macro repeat count>
    <#list 1..count as x>
        <#nested x, x/2, x==count>
    </#list>
</#macro>

使用的时候:

<@repeat count=4 ; c, halfc, last>
    ${c}.${halfc}<#if last> Last!</#if>
</@repeat>

那么将会输出:

\1. 0.5
\2. 1
\3. 1.5
\4. 2 Last!

例子:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/nested.ftl


    <h3>一个宏可以使用多个循环变量</h3>
    <#macro repeat count>
        <#list 1..count as x>
            <#nested x, x/2, x==count><br>
        </#list>
    </#macro>
    <p>
        <@repeat count=4 ; c, half, last>
            ${c}. ${half} <#if last> Last!</#if>
        </@repeat>
    </p>

如果分号后指定的变量少了或多了,多出的会忽略掉;

    @repeat count=4 ; c, half>
        ${c}. ${half}
    </@repeat>
4.1.6 自定义指令和宏进阶

你也可以在FTL中定义方法,参见function指令;

也许你对命名空间感兴趣。命名空间可以帮助你组织和重用你经常使用的宏;

4.2 在模板中定义变量

可以访问一个在模板里定义的变量,就像是访问数据模型根上的变量一样。这个变量比定义在数据模型中的同名参数具有更高的优先级。如果你恰巧定义了一个名为“foo”的变量,而在数据模型中也有一个名为“foo”的变量,那么模板中的变量就会将数据模型根上的变量隐藏(而不是覆盖!)。

在模板中可以定义三种类型的变量:

  • 简单变量:它能从模板中的任何位置访问,或者从使用include指令引入的模板访问。可以使用assignmacro指令来创建或替换这些变量。

  • 局部变量:它们只能被设置在宏定义体内,而且只在宏内可见。一个局部变量的生命周期只是宏的调用过程。可以使用local来创建或替换局部变量。
  • 循环变量:循环变量是由指令(如list)自动创建的,而且它们只在指令的开始和结束标记内有效。宏的参数是局部变量而不是循环变量。

使用assign创建和替换变量;

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl


    <h3>简单变量</h3>
    <p>
        <#assign x=1> <#--创建变量x-->
        ${x}<br>
        <#assign x=x+3> <#--替换变量x-->
        ${x}
    </p>

输出:

简单变量
1
4 

局部变量也会隐藏(而不是)同名的简单变量。循环变量也会隐藏(不是覆盖)同名的局部变量和简单变量。

实例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl


    <#macro test>
        2. ${x}<br>
        <#local x="local">
        3. ${x}<br>
        <#list ["loop"] as x>
            4. ${x}<br>
        </#list>
        5. ${x}<br>
    </#macro>
    <p>
        <#assign x="plain">
        1. ${x}<br>
        <@test/>
        6. ${x}<br>
        <#list ["loop"] as x>
            7. ${x}<br>
            <#assign x="plain2"> <#--在这里替换了简单变量x-->
            8. ${x}<br>
        </#list>
        9. ${x}
    </p>

输出:

1. plain
2. plain
3. local
4. loop
5. local
6. plain
7. loop
8. loop
9. plain2

内部循环可以隐藏外部循环的变量;

实例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl


        <#list ["loop 1"] as x>
            ${x}<br>
            <#list ["loop 2"] as x>
                ${x}<br>
                <#list ["loop 3"] as x>
                    ${x}<br>
                </#list>
                ${x}<br>
            </#list>
            ${x}<br>
        </#list>

输出:

loop 1
loop 2
loop 3
loop 2
loop 1

有时发生一个变量隐藏数据模型中的同名变量,但是如果想访问数据模型中的变量,就可以使用特殊变量globals

实例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/variable.ftl


        <#assign user="Cindy">
        ${user}, ${.globals.user}

/FreeMarker-hello-web/src/main/java/org/yejq/fre/service/Exercises.java


    public void testVariable(Model model){
        model.addAttribute("user", "lucy");
    }

测试:http://localhost/test/4/variable/testVariable,输出结果:

Cindy, lucy

4.3 命名空间

4.3.1 简介

如果想创建可以重复使用的宏、函数和其他变量的集合,通常用术语来说就是引用library库,使用命名空间是必然的;

4.3.2 创建一个库

实例:/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl


<#macro copyright date>
    <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
</#macro>
<#assign mail="[email protected]">

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl


    <h3>使用import导入</h3>
    <p>
        <#import "../lib/my_test.ftl" as my>
        <@my.copyright date="2014-2016"/>
        ${my.mail}
    </p>

输出:

使用import导入

Copyright (C) 2014-2016 Julia Smith. All rights reserved.

[email protected] 

测试不同的命名空间;

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl


<#macro copyright date>
    <p>Copyright (C) ${date} Julia Smith. All rights reserved.
    <br>Mail: ${mail}
    </p>
</#macro>
<#assign mail="[email protected]">

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl


    <h3>演示不同的命名空间</h3>
    <p>
        <#import "../lib/my_test.ftl" as my>
        <#assign mail="[email protected]">
        <@my.copyright date="2014-2016"/>
        ${my.mail}<br>
        ${mail}
    </p>

测试:http://localhost/test/4/namespace/null

4.3.3 在引入的命名空间上编写变量

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl


        <#import "../lib/my_test.ftl" as my>
        ${my.mail}<br>
        <#assign mail="[email protected]" in my>
        ${my.mail}

http://localhost/test/4/namespace/null,输出:

[email protected]
[email protected] 
4.3.4 命名空间和数据模型

数据模型中的变量在任何位置都是可见的。如果在数据模型中有一个名为user的变量,那么lib/my_test.ftl也能访问TA。

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/lib/my_test.ftl


<#macro copyright date>
    <p>Copyright (C) ${date} ${user} Julia Smith. All rights reserved.
    <br>Mail: ${mail}
    </p>
</#macro>
<#assign mail="${user}@acme.com">

/FreeMarker-hello-web/src/main/java/org/yejq/fre/service/Exercises.java


    public void testNamespace(Model model){
        model.addAttribute("user", "lucy");
    }

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl


    <h3>数据模型中的变量在任何位置都是可见的</h3>
    <p>
        <#import "../lib/my_test.ftl" as my>
        <@my.copyright date="2014-2015"/>
    </p>

测试:http://localhost/test/4/namespace/testNamespace?11,输出结果:

Copyright (C) 2014-2015 lucy Julia Smith. All rights reserved.
Mail: [email protected] 
4.3.5 命名空间的生命周期

命名空间由使用的import指令中所写的路径来识别。如果想多次import这个路径,那么只会为第一次的import引用创建命名空间执行模板。后边相同的路径的import只是创建一个哈希表当做访问相同命名空间的“门”。

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/namespace.ftl


    <h3>命名空间的声明周期</h3>
    <p>
        <#import "/lib/my_test.ftl" as my>
        <#import "/lib/my_test.ftl" as foo>
        <#import "/lib/my_test.ftl" as bar>
        <#--只会在第一次创建命名空间执行模板,之后相同路径的import只是创建哈希表当做访问相同命名空间的"门"-->
        ${my.mail}, ${foo.mail}, ${bar.mail}<br>
        <#assign mail="[email protected]" in my>
        ${my.mail}, ${foo.mail}, ${bar.mail}<br>
        <#--其中一个命名空间的值修改了,所有都相应修改,就像java里边的对象引用修改一样-->
        <#assign mail="[email protected]" in foo>
        ${my.mail}, ${foo.mail}, ${bar.mail}
    </p>

访问:http://localhost/test/4/namespace/null,输出:

命名空间的声明周期

[email protected], [email protected], [email protected]
[email protected], [email protected], [email protected]
[email protected], [email protected], [email protected] 

还要注意命名空间是不分层次的,它们相互之间是独立存在的。那么,如果在命名空间N1中import命名空N2,那N2也不在N1中,N1只是通过哈希表访问N2。这和主命名空间中importN2,然后直接访问命名空间N2是一样的过程。

每一次模板的执行过程,它都有一个私有的命名空间的集合。每一次模板执行工作都是一个分离且有序的过程,它们仅仅存在一段很短的时间,同时页面用以呈现内容,然后就和所有填充过的命名空间一起消失了。

4.3.6 为他人编写库

http://freemarker.org/libraries.html

标准库路径的格式:

/lib/yourcompany.com/your_library.ftl

如果你的公司的主页是www.example.com,那么;

/lib/example.com/widget.ftl
/lib/example.com/commons/string.ftl

一个重要的规则是路径不应该包含大写字母,winForm改成win_form;

4.4 空白处理

4.4.1 简介

来看看这个模板;

按照Freemarker规则输出后是:

这么多多余的空白是很令人头疼,而且增加处理后的HTML文件大小也是没必要的;

FreeMarker提供了以下工具来处理这个问题:

  • 忽略某些文件的空白的工具(解析阶段空白就被移除了)

    • 剥离空白:这个特性会自动忽略FTL标签周围的空白。这个特性可以通过模板来随时使用和禁用;

    • 微调指令:t, rtlt,使用这些指令可以明确告诉FreeMarker去忽略某些空白;
    • FTL参数strip_text:这将从模板中删除所有顶级文本。对模板来说很有用,它包含某些定义的宏,因为它可以移除宏定义和其他顶级指令中的换行符,这样可以提高模板的可读性;
  • 从输出移除空白的工具(移除临近的空白)
    • compress指令
4.4.2 剥离空白

它会自动忽略两种典型的空白:

  • 缩进空白和行末尾的尾部空白;如果这行上包含<#if ...>x,那么空白不会忽略,因为x不是标签。而一行上有<#if ...> <#list ...>,这样也不会忽略空白,因为标签之间的空格是嵌入空白;

  • 加在这些指令之间的空白会被忽略:macro,function,assign,global,local,ftl,import;

使用剥离空白之后,上面的例子输出是:

剥离空白功能可以在ftl指令在模板中开启或关闭。默认开启。开启剥离空白不会降低模板执行的效率,剥离空白的操作在模板加载时就已经完成了。

剥离空白可以为单独的一行关闭,就是使用nt指令;

4.4.3 使用compress指令

和剥离空白相反,这个工作是直接基于生产的输出内容,而不是对模板进行的。它会强势地移除缩进,空行和重复的空格/制表符;

对于下边这段代码:

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/compress.ftl


    <h2>使用compress指令,在输出的内容中移除缩进,空行和空格/制表符</h2>
    <p>
        <#compress>
            <#assign users = [{"name":"Joe", "hidden":false},
                            {"name":"James Bond", "hidden":true},
                            {"name":"Julia", "hidden":false}]>
            list of users:<br>
            <#list users as user>
                <#if !user.hidden>
                    - ${user.name}<br>
                </#if>
            </#list>
        </#compress>
    </p>

输出:

        <h2>使用compress指令,在输出的内容中移除缩进,空行和空格/制表符</h2>
    <p>
        list of users:<br>
- Joe<br>
- Julia<br> </p>

由于向下兼容性,名称为compress的用户自定义指令是存在的,有个single_line的属性,如果设置为true,那么会移除其中的换行符;


    <h3>@compress的属性single_line</h3>
    <p>
        <@compress single_line=true>
            <#assign users = [{"name":"Joe", "hidden":false},
                            {"name":"James Bond", "hidden":true},
                            {"name":"Julia", "hidden":false}]>
            list of users:<br>
            <#list users as user>
                <#if !user.hidden>
                    - ${user.name}<br>
                </#if>
            </#list>
        </@compress>
    </p>

貌似不起作用,反而#compress是single_line;

4.5 替换(方括号)语法

版本2.3.4之后才有;

在指令和注释中使用[]代替<>

[#list ...]...[/list]
[@mymacro .../]
[#-- the comment --]

为了使用这种语法从而代替默认语法,那么就需要用[#ftl]来开始模板。

/FreeMarker-hello-web/src/main/webapp/WEB-INF/ftl/4/ftl.ftl


[#ftl]
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
</head>
<body>
    <h3>替换语法</h3>
    <p>
        [#assign users = [{"name":"Joe", "hidden":false},
                            {"name":"James Bond", "hidden":true},
                            {"name":"Julia", "hidden":false}]]
        list of users:<br>
        [#list users as user]
            [#if !user.hidden]
                - ${user.name}<br>
            [/#if]
        [/#list]
    </p>
</body>
</html>

测试:http://localhost/test/4/ftl/null

项目

  1. P1:https://github.com/yejq/FreeMarker-hello-java.git

  2. P2:https://github.com/yejq/FreeMarker-hello-web.git
时间: 2024-10-06 22:37:50

FreeMarker笔记 第四章 其它的相关文章

Android群英传笔记——第四章:ListView使用技巧

Android群英传笔记--第四章:ListView使用技巧 近期也是比較迷茫.可是有一点点还是要坚持的,就是学习了.近期离职了,今天也是继续温习第四章ListView,也拖了事实上也挺久的了,listview可谓是老牌大将了,非常多的应用场景都要使用它,他也是我们用得最多的控件之中的一个了,尽管如今出来了一个RecyclerView,可是ListView的地位一时半会儿还是撼动不了的.这就促使我们更加应该去把他掌握了 一.Listview经常使用优化技巧 我们一步步来把ListView学习好

《Linux Shell脚本攻略》 笔记 第四章:高效文本处理

<Linux Shell脚本攻略> 笔记 第四章:高效文本处理 1.IP地址的正则表达式: [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} 2.grep用法 //在多级目录中对文本进行递归检索 [[email protected] program_test]# grep "yang" ./ -Rn ./test.txt:6:laoyang ./right.txt:1:1 yang man //忽略大小写匹配 [[email pr

人月神话阅读笔记—第四章

人月神话阅读笔记-第四章 ------2016.6.14 概念的完整性是很重要的,为了反应一系列连贯的设计思路,可以省略一些不规则的特性和改进,不提倡独立和无法整合的系统,最需要的是在整体概念上的完整性要求. 获得概念的完整性时,会出现一种情况,编程系统使计算机更加好用,但是功能比较多的时候,软件外部描述就会比系统本身大很多:但是功能太少,不能满足需求,但是都需要满足概念上的完整性. 在进行概念的完整性时,产品设计需要由一个人或者少数几个人来实现,但是对于大型的系统,需要将设计方法.体系结构的工

Java学习笔记—第四章

第四章  变量和常量 1. Java的访问控制修饰符 使用访问控制修饰符可以限制数据的访问权限.访问控制修饰符有4个等级:private.protected.    public和默认(不指定修饰符). 类型/权限 private protected public 默认 所属类 可访问 可访问 可访问 可访问 同一个包中的其他类(包括子类) 不可访问 可访问 可访问 可访问 不同包中的子类 不可访问 可访问 可访问 不可访问 不同包中的非子类 不可访问 不可访问 可访问 不可访问 2. 变量:变

[现代操作系统笔记][第四章文件系统]

第四章 文件系统 4.1 文件 从用户角度来考察文件.用户如何使用文件,文件有那些特性. 4.1.1 文件命名 文件一种抽象机制,提供了一种在磁盘上保留信息而且以后方便读取的方法. 有的文件系统区分大小写,有的则不区分 Unix是前者,MS-DOS是后者 FAT-16,FAT-32,NTFS. FAT-16(File Allocation Table,文件配置表) : Windows 95 FAT-32 : Windows 98 NTFS(New Technology File System,新

《Linux内核设计与实现》读书笔记 第四章 进程调度

第四章进程调度 进程调度程序可看做在可运行太进程之间分配有限的处理器时间资源的内核子系统.调度程序是多任务操作系统的基础.通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的效果. 最大限度地利用处理器时间的原则是,只要有可以执行的进程,那么总会有进程在执行. 4.1多任务 多任务操作系统就是能同时并发地交互执行多个进程的操作系统.多任务能使多个进程处于堵塞或者睡眠状态.就是任务位于内存内但是不被执行,直到某一事件发生. 分类: l  非抢占式:除非进程自己主动停止运行

【PMP】Head First PMP 学习笔记 第四章

第四章 项目整合管理 项目经理每天的工作 力保项目顺利进行 紧密监督以确保计划进行 计划本身不完善需要予以修正 即使是更大的项目的子项目,也要有收尾的过程 6个整合管理过程 整合管理划分为6个过程,项目经理必须掌握的核心职责. 1.制订项目章程 授权你展开工作的文档,通常由赞助人(为项目提供资金的人)交给你. 2.制订项目管理计划 项目管理计划涵盖了所有知识领域,很大一部分就是告诉你在出现问题时如何处理变更. 3.指导和管理项目执行 确保所有人都在做他们本该做的事,项目创建的产品和项目确实满足干

《利用python进行数据分析》读书笔记--第四章 numpy基础:数组和矢量计算

第四章 Numpy基础:数组和矢量计算 实话说,用numpy的主要目的在于应用矢量化运算.Numpy并没有多么高级的数据分析功能,理解Numpy和面向数组的计算能有助于理解后面的pandas.按照课本的说法,作者关心的功能主要集中于: 用于数据整理和清理.子集构造和过滤.转换等快速的矢量化运算 常用的数组解法,如排序.唯一化.集合运算等 高效的描述统计和数据聚合/摘要运算 用于异构数据集的合并/连接运算的数据对齐和关系型数据运算 将条件逻辑表述为数组表达式(而不是带有if-elif-else分支

APUE读书笔记-第四章 文件和目录

到第四章了,不知什么时候才能把这本书看完,耽误的时间太多了. 第四章是在第三章的基础上,主要描述文件系统的其他性质和文件的性质. 4.2 stat.fstat.fstatat.lstat函数 首先来看看这四个函数的原型: #include <sys/stat.h> ///usr/include/x86_64-linux-gnu/sys/ int stat (const char *__restrict __file, struct stat *__restrict __buf) int fst