您当前的位置: 首页 >  Java

wespten

暂无认证

  • 1浏览

    0关注

    899博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

JavaWeb开发与代码的编写(八)

wespten 发布时间:2019-08-21 12:52:56 ,浏览量:1

JavaWeb开发与代码的编写(八)

模板引擎比较

1、FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java 等,在spring4.0中推荐使用thymeleaf来做前端模版引擎。 2、JSP技术spring boot 官方是不推荐的,原因有三: 2.1. 在tomcat上,jsp不能在嵌套的tomcat容器解析即不能在打包成可执行的jar的情况下解析 2.2. Jetty 嵌套的容器不支持jsp 2.3. Undertow 3、反正就是spring推荐themleaf,就学学themleaf。

在java领域,表现层技术主要有三种:jsp、freemarker、velocity。

jsp是大家最熟悉的技术 优点: 1、功能强大,可以写java代码 2、支持jsp标签(jsp tag) 3、支持表达式语言(el) 4、官方标准,用户群广,丰富的第三方jsp标签库 5、性能良好。jsp编译成class文件执行,有很好的性能表现 缺点: jsp没有明显缺点,非要挑点骨头那就是,由于可以编写java代码,如使用不当容易破坏mvc结构。

velocity是较早出现的用于代替jsp的模板语言 优点: 1、不能编写java代码,可以实现严格的mvc分离 2、性能良好,据说比jsp性能还要好些 3、使用表达式语言,据说jsp的表达式语言就是学velocity的 缺点: 1、不是官方标准 2、用户群体和第三方标签库没有jsp多。 3、对jsp标签支持不够好 4、已经很久很久没有维护了。

freemarker 优点: 1、不能编写java代码,可以实现严格的mvc分离 2、性能非常不错 3、对jsp标签支持良好 4、内置大量常用功能,使用非常方便 5、宏定义(类似jsp标签)非常方便 6、使用表达式语言 缺点: 1、不是官方标准 2、用户群体和第三方标签库没有jsp多

选择freemarker的原因: 1、性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。 2、宏定义比jsp tag方便 3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便 4、支持jsp标签 5、可以实现严格的mvc分离

thymeleaf Thymeleaf是个XML/XHTML/HTML5模板引擎,可以用于Web与非Web应用。 Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。Thymeleaf的可扩展性也非常棒。你可以使用它定义自己的模板属性集合,这样就可以计算自定义表达式并使用自定义逻辑。这意味着Thymeleaf还可以作为模板引擎框架。 thymeleaf优点:静态html嵌入标签属性,浏览器可以直接打开模板文件,便于前后端联调。springboot官方推荐方案。thymeleaf缺点:模板必须符合xml规范,就这一点就可以判死刑!太不方便了!js脚本必须加入/

 

freemarker简介

   在互联网软件内容网站中 一般首页的访问量大,为了提供首页的访问效率,一般 首页的内容以及其中的新闻等信息都可以实现html静态化 浏览器访问时 设置浏览器的缓存策略和生成静态页面的周期一致 可以使访问效率大大提升 同时配合cdn处理图片 js css等资源 可以在首页访问时 理论完全脱离数据库  降低应用压力

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

模板编写为FreeMarker Template Language (FTL)。 在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。 这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。

  原理图:

   

上图如果Template换成Jsp,对于大多数JavaEE开发者来说,会显得非常熟悉。FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。 它完全可以替代Jsp,实现页面的静态化。

实际应用案例

  比如 新闻门户网站  后台添加新闻时  通过新闻数据+预定义的模板ftl 生成静态的html  新闻数据插入数据库 绑定该html新闻保存的html位置 如果该新闻被推送到首页 

生成首页静态html时  将该新闻的超链接指向 该文章的静态html

下图访问服务(nginx集群+cdn缓存+后台应用服务器  MQ表示队列)

 

freeemarker模板引擎的引入

  freemarker是一套前端模板引擎,在使用时,要先在web项目中添加freemarker.jar的依赖。

       利用IDEA创建工程,如果是普通项目则将 freemarker.jar 添加的 lib 目录下。如果创建为Maven项目,则可以在pom文件中添加:


  org.freemarker
  freemarker
  2.3.28

      如果是结合Spring使用FreeMarker,则需要额外添加依赖:


    org.springframework
    spring-context-support
    ${spring.version}

  我在这里主要演示spring-mvc整合freemarker模板引擎。项目案例的文件包结构示意图如下

首先,在工程的web.xml文件中进行配置:



    
    
        spring-mvc
        org.springframework.web.servlet.DispatcherServlet
        
            contextConfigLocation
            classpath:config/springmvc-config.xml
        
        1
    
    
        spring-mvc
        *.html
    
    
    
        encoding
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        
    
    
        encoding
        /*
    

然后配置springmvc-config.xml文件:



 
    
 
    
    
 
    
        
        
    
    
    
        
        
        
    

网站首页如下:



  
    $Title$
  
  
    test1
    
  test2
    
    test3
    
    test4
    
    test5
    
    test6
    
    test7
    
    test8
    
    test9
  

 后台Java代码如下:

import java.util.List;
 
/**
 * 实体类
 */
public class User {
    private String username,usersex;
    private int userage;
    private String nation,hometown;
    private List userhbs;
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getUsersex() {
        return usersex;
    }
 
    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }
 
    public int getUserage() {
        return userage;
    }
 
    public void setUserage(int userage) {
        this.userage = userage;
    }
 
    public String getNation() {
        return nation;
    }
 
    public void setNation(String nation) {
        this.nation = nation;
    }
 
    public String getHometown() {
        return hometown;
    }
 
    public void setHometown(String hometown) {
        this.hometown = hometown;
    }
 
    public List getUserhbs() {
        return userhbs;
    }
 
    public void setUserhbs(List userhbs) {
        this.userhbs = userhbs;
    }
}
-----------------------------------------------
package com.itszt.controller;
 
import com.itszt.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;
 
/**
 * 控制器
 */
@RequestMapping("testController")
@Controller
public class TestController {
    @RequestMapping("test1")
    public String test1(HttpServletRequest request, HttpSession session){
        request.setAttribute("data1","dd");
        request.setAttribute("data2","ff");
        session.setAttribute("data3","vv");
        return "hello";
    }
 
    @RequestMapping("test2")
    public String test2(Model model){
        User user=new User();
        user.setUsername("张三");
        user.setUsersex("男");
        user.setUserage(18);
        user.setNation("汉族");
        user.setHometown("中国北京市通州");
 
        List userhbs=new ArrayList();
        userhbs.add("爬山");
        userhbs.add("游泳");
        userhbs.add("滑冰");
 
        user.setUserhbs(userhbs);
 
        Date date=new Date();
 
        model.addAttribute("data1",user);
        model.addAttribute("data2",date);
 
        return "test2";
    }
 
    @RequestMapping("test3")
    public String test3(Model model){
        List list=new ArrayList();
        list.add("爬山");
        list.add("游泳");
        list.add("打篮球");
        model.addAttribute("data1",list);
 
        Map map=new HashMap();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        model.addAttribute("data2",map);
 
        return "test3";
    }
 
    @RequestMapping("test4")
    public String test4(Model model){
        User user1=new User();
        user1.setUsername("Jim");
        user1.setUsersex("male");
        user1.setUserage(18);
 
        User user2=new User();
        user2.setUsername("Tom");
        user2.setUsersex("female");
        user2.setUserage(19);
 
        User user3=new User();
        user3.setUsername("Stone");
        user3.setUsersex("male");
        user3.setUserage(20);
 
        List users=new ArrayList();
        users.add(user1);
        users.add(user2);
        users.add(user3);
 
        model.addAttribute("users",users);
 
        return "test4";
    }
 
    @RequestMapping("test5")
    public String test5(Model model){
        model.addAttribute("str1","ABCD");
        model.addAttribute("str2","abcd");
        model.addAttribute("intData1",159);
        model.addAttribute("intData2",753);
        return "test5";
    }
 
    @RequestMapping("test6")
    public String test6(Model model){
        model.addAttribute("scoreMath",65);
        model.addAttribute("scoreJava",90);
        return "test6";
    }
 
    @RequestMapping("test7")
    public String test7(Model model){
        model.addAttribute("htmlData","你好,霍金先生,请一路走好!");
        model.addAttribute("lowerData","ABCD");
        model.addAttribute("upperData","abcd");
        model.addAttribute("cfData","abcd");
        model.addAttribute("intData",123.456);
        return "test7";
    }
 
 
    @RequestMapping("test8")
    public String test8(Model model){
        return "test8_1";
    }
 
    @RequestMapping("test9")
    public String test9(Model model){
        model.addAttribute("data1","我是后台返回的一个数据");
        return "test9";
    }
 
 
}

WEB-INF/templates下的模板文件如下:

hello.ftl


    $Title$


你好啊。。我是第一个FreeMarker

动态数据展示1:${data1}

动态数据展示2:${data2}

动态数据展示2:${data3}


-----------------------------------------
test2.ftl


    $Title$


你好啊。。我来显示javabean和日期

动态数据展示1:${data1.username}--${data1.usersex}--${data1.userage}--${data1.nation}--${data1.hometown}

动态数据展示2:${data2?string("yyyy-MM-dd HH:mm:ss")}
 


----------------------------------------------
test3.ftl


    $Title$


你好啊。。我来显示javabean和日期

动态数据展示1:
该集合中元素的个数:${data1?size} 个

    
        ${hb_index}.${hb}
    


动态数据展示2:
该集合中元素的个数:${data2?size} 个

   
   ${mapKey}--${data2[mapKey]}
   



---------------------------------------------
test4.ftl


    $Title$


你好啊。。我来显示date和javabean的FreeMarker

动态数据展示:
该集合中共有 : ${users?size}  个

    ${user_index+1}. ${user.username}  ${user.usersex} ${user.userage}



---------------------------------------------------
test5.ftl


    $Title$


你好啊。。我来显示date和javabean的FreeMarker

演示字符串拼接
${"前面的东西${str1}"}

${"${str2}后面的东西"}

${intData1}


-------------------------------------------------
test6.ftl


    $Title$


你好啊。。我来显示date和javabean的FreeMarker


    数学优秀

数学一般

数学不及格



    优秀学生

一般学生

不及格学生



------------------------------------------------
test7.ftl


    $Title$


你好啊。。我来显示date和javabean的FreeMarker

${htmlData}

${htmlData?html}

${upperData?upper_case}

${lowerData?lower_case}

${cfData?cap_first}

${intData?int}


----------------------------------------
test8_1.ftl


    $Title$



我是8-1的东西。。。。嘻嘻哈哈



---------------------------------------
test8_2.ftl


    $Title$



我是8-2的东西。。。。


------------------------------------------
test9.ftl


    $Title$



演示macro自定义标签:

1

 
xxxxxxxxx
 



-----------------------------------------
test10.ftl


    $Title$




 




    我是test10.ftl


    ${data} 





总的来说,在在.ftl的页面里,可以这样操作传递过来的数据:

直接显示数据或数据的hashcode  ${dataName}
显示自定义类型对象的属性    ${dataName.username}
显示日期型数据 ${dateData?String("yyyy-MM-dd HH:mm:ss")}
显示List型数据   ${listData?size}    
        
            ${hb_index} ${hb}
        
显示Map型数据    ${mapData?size}
        
            ${mapKey}   ${mapData[mapKey]}
        
显示自定义类型对象的list集合:
        
            ${user_index}   ${user.username}
        
字符串拼接   ${"前面的东西${strData}"}
if判断    
        数学优秀
        
        数学一般
        
        数学不及格
    
其他函数    ${htmlData?html}
        ${upperData?upper_case}
        ${lowerData?lower_case}
        ${cfData?cap_first}
        ${intData?int}
引入其他.ftl页面  在test1.ftl中写入
            
macro自定义标签  test2.ftl页面中写:
            
            ${data} 
            
        test1.ftl页面中写:
            
            haha

 

FTL指令

FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成:

1.文本:直接输出的部分
2.注释:格式部分,不会输出
3.插值:即${...}或#{...}格式的部分,将使用数据模型中的部分替代输出
4.FTL指令:FreeMarker指定,和HTML标记类似,名字前加#予以区分,不会输出

下面是一个FreeMarker模板的例子,包含了以上所说的4个部分:


    
        Welcome!
    
    
        
        
        Welcome ${user} !
        

We have these animals:

  • ${being.name} for ${being.price} Euros
  • FTL指令规则

    FreeMarker中,使用FTL标签来使用指令,FreeMarker3FTL标签,这和HTML标签是完全类似的.

    开始标签:
    结束标签:
    空标签:
    

    实际上,使用标签时前面的符号#也可能变成@,如果该指令是一个用户指令而不是系统内建指令时,应将#符号改成@符号.

    使用FTL标签时,应该有正确的嵌套,而不是交叉使用,这和XML标签的用法完全一样.如果全用不存在的指令,FreeMarker不会使用模板输出,而是产生一个错误消息.FreeMarker会忽略FTL标签中的空白字符.值得注意的是< , />和指令之间不允许有空白字符.

    插值规则

    FreeMarker的插值有如下两种类型:

    通用插值${expr};
    数字格式化插值:#{expr}或#{expr;format}

    通用插值

    对于通用插值,又可以分为以下4种情况:

    1.插值结果为字符串值:直接输出表达式结果
    2.插值结果为数字值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串   函数格式化单个插值,如下面的例子:
    
    
    
    ${answer}
    ${answer?string} 
    ${answer?string.number}
    ${answer?string.currency}
    ${answer?string.percent}
    ${answer}
    

    输出结果是:

    $42.00
    $42.00
    42
    $42.00
    4,200%
    
    3.插值结果为日期值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:
    
    ${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")}
    ${lastUpdated?string("EEE, MMM d, ''yy")}
    ${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")}
    

    输出结果是:

    2008-04-08 08:08:08 Pacific Daylight Time
    Tue, Apr 8, '03
    Tuesday, April 08, 2003, 08:08:08 PM (PDT)
    
    4.插值结果为布尔值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:
    
    
    ${foo?string("yes", "no")}
    

    输出结果是:

    yes

    数字格式化插值

    数字格式化插值可采用#{expr;format}形式来格式化数字,其中format可以是:

    mX:小数部分最小X位
    MX:小数部分最大X位
    

    如下面的例子:

    
    
    #{x; M2} 
    #{y; M2} 
    #{x; m2} 
    #{y; m2} 
    #{x; m1M2} 
    #{x; m1M2} 

     

    表达式

    表达式是FreeMarker模板的核心功能,表达式放置在插值语法${}之中时,表明需要输出表达式的值;表达式语法也可与FreeMarker标签结合,用于控制输出.实际上FreeMarker的表达式功能非常强大,它不仅支持直接指定值,输出变量值,也支持字符串格式化输出和集合访问等功能.

    直接指定值

    使用直接指定值语法让FreeMarker直接输出插值中的值,而不是输出变量值.直接指定值可以是字符串,数值,布尔值,集合和MAP对象.

    字符串
    

    直接指定字符串值使用单引号或双引号限定,如果字符串值中包含特殊字符需要转义,看下面的例子:

    ${"我的文件保存在C:\\盘"}
    ${'我名字是\"annlee\"'}
    

    输出结果是:

    我的文件保存在C:\盘
    我名字是"annlee"
    

    FreeMarker支持如下转义字符:

    \"; 双引号(u0022)
    \'; 单引号(u0027)
    \\; 反斜杠(u005C)
    \n; 换行(u000A)
    \r; 回车(u000D)
    \t; Tab(u0009)
    \b; 退格键(u0008)
    \f; Form feed(u000C)
    \l; <
    \g; >
    \a; &
    \{; {
    \xCode; 直接通过4位的16进制数来指定Unicode码,输出该unicode码对应的字符.
    

    如果某段文本中包含大量的特殊符号,FreeMarker提供了另一种特殊格式:可以在指定字符串内容的引号前增加r标记,在r标记后的文件将会直接输出。看如下代码:

    ${r"${foo}"}
    ${r"C:\foo\bar"}
    

    输出结果是:

    ${foo}
    C:\foo\bar
    
    数值
    

    表达式中的数值直接输出,不需要引号.小数点使用"."分隔,不能使用分组","符号.FreeMarker目前还不支持科学计数法,所以"1E3"是错误的.在FreeMarker表达式中使用数值需要注意以下几点:

    数值不能省略小数点前面的0,所以".5"是错误的写法
    
    数值8 , +8 , 8.00都是相同的
    
    布尔值,直接使用true和false,不使用引号.
    
    集合, 集合以方括号包括,各集合元素之间以英文逗号","分隔
    
    如下为集合元素遍历的例子:
    
    
        ${x}
    
    
    输出结果是:
    
    星期一
    星期二
    星期三
    星期四
    星期五
    星期六
    星期天
    
    除此之外,集合元素也可以是表达式,例子如下:
    
    [2 + 2, [1, 2, 3, 4], "whatnot"]
    
    还可以使用数字范围定义数字集合,如2..5等同于[2, 3, 4, 5], 但是更有效率。
    
        注意,使用数字范围来定义集合时无需使用方括号,数字范围也支持反递增的数字范围,如5..2
    
    Map对象,Map对象使用花括号包括,Map中的key-value对之间以英文冒号":"分隔,多组key-value对之间以英文逗号","分隔。
    
    下面是一个例子:
    
    {"语文":78, "数学":80}
    
    Map对象的key和value都是表达式,但是key必须是字符串。

     

    输出变量值

    FreeMarker的表达式输出变量时,这些变量可以是顶层变量,也可以是Map对象中的变量,还可以是集合中的变量,并可以使用点(.)语法来访问Java对象的属性。下面分别讨论这些情况:

    1. 顶层变量

    所谓顶层变量就是直接放在数据模型中的值,例如有如下数据模型:

    Map root = new HashMap();   //创建数据模型
    root.put("name","annlee");   //name是一个顶层变量
    

    对于顶层变量,直接使用${variableName}来输出变量值,变量名只能是字母,数字,下划线,$,@#的组合,且不能以数字开头号.为了输出上面的name的值,可以使用如下语法:

    ${name}
    

    2. 输出集合元素

    如果需要输出集合元素,则可以根据集合元素的索引来输出集合元素,集合元素的索引以方括号指定。

    假设有索引:["星期一","星期二","星期三","星期四","星期五","星期六","星期天"],该索引名为week,如果需要输出星期三,则可以使用如下语法:

    ${week[2]} //输出第三个集合元素
    

    此外,FreeMarker还支持返回集合的子集合,如果需要返回集合的子集合,则可以使用如下语法:

    week[3..5] //返回week集合的子集合,子集合中的元素是week集合中的第4-6个元素
    

    3. 输出Map元素

    这里的Map对象可以是直接HashMap的实例,甚至包括JavaBean实例,对于JavaBean实例而言,我们一样可以把其当成属性为key,属性值为valueMap实例.为了输出Map元素的值,可以使用点语法或方括号语法.假如有下面的数据模型:

    Map root = new HashMap();
    Book book = new Book();
    Author author = new Author();
    author.setName("annlee");
    author.setAddress("gz");
    book.setName("struts2");
    book.setAuthor(author);
    root.put("info","struts");
    root.put("book", book);
    

    为了访问数据模型中名为struts2的书的作者的名字,可以使用如下语法:

    book.author.name //全部使用点语法
    book["author"].name
    book.author["name"] //混合使用点语法和方括号语法
    book["author"]["name"] //全部使用方括号语法
    

    使用点语法时,变量名字有顶层变量一样的限制,但方括号语法没有该限制,因为名字可以是任意表达式的结果.

     

    字符串操作

    FreeMarker的表达式对字符串操作非常灵活,可以将字符串常量和变量连接起来,也可以返回字符串的子串等。

    字符串连接有两种语法:

    使用${..}或#{..}在字符串常量部分插入表达式的值,从而完成字符串连接.
    直接使用连接运算符+来连接字符串
    

    例如有如下数据模型:

    Map root = new HashMap(); 
    root.put("user","annlee");
    

    下面将user变量和常量连接起来:

    ${"hello, ${user}!"}   //使用第一种语法来连接
    ${"hello, " + user + "!"} //使用+号来连接
    

    上面的输出字符串都是hello,annlee!,可以看出这两种语法的效果完全一样. 值得注意的是,${..}只能用于文本部分,不能用于表达式,下面的代码是错误的:

    Wow!
    Wow!
    

    应该写成:

    Wow!
    

    截取子串可以根据字符串的索引来进行,截取子串时如果只指定了一个索引值,则用于取得字符串中指定索引所对应的字符;如果指定两个索引值,则返回两个索引中间的字符串子串。假如有如下数据模型:

    Map root = new HashMap(); 
    root.put("book","struts2,freemarker");
    

    可以通过如下语法来截取子串:

    ${book[0]}${book[4]}   //结果是su
    ${book[1..4]}     //结果是tru

     

    集合连接运算符

    这里所说的集合运算符是将两个集合连接成一个新的集合,连接集合的运算符是+,看如下的例子:

    
        ${x}
    
    

    输出结果是:

    星期一 星期二 星期三 星期四 星期五 星期六 星期天

     

    Map连接运算符

    Map对象的连接运算符也是将两个Map对象连接成一个新的Map对象,Map对象的连接运算符是+,如果两个Map对象具有相同的key,则右边的值替代左边的值.看如下的例子:

    
    语文成绩是${scores.语文}
    数学成绩是${scores.数学}
    Java成绩是${scores.Java}
    

    输出结果是:

    语文成绩是86
    数学成绩是87
    Java成绩是93

     

    算术运算符

    FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %看如下的代码:

    
    ${ x * x - 100 }
    ${ x /2 }
    ${ 12 %10 }
    

    输出结果是:

    -75   2.5   2
    

    在表达式中使用算术运算符时要注意以下几点:

    运算符两边的运算数字必须是数字
    使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如: ${3 + "5"},结果是:35
    

    使用内建的int函数可对数值取整,如:

    
    ${ (x/2)?int }
    ${ 1.1?int }
    ${ 1.999?int }
    ${ -1.1?int }
    ${ -1.999?int }
    

    结果是:

    2 1 1 -1 -1

     

    比较运算符

    表达式中支持的比较运算符有如下几个:

    =或者==:判断两个值是否相等.
    !=:判断两个值是否不等.
    >或者gt:判断左边值是否大于右边值
    >=或者gte:判断左边值是否大于等于右边值
    解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:y)>。
    
     

    逻辑运算符

    逻辑运算符有如下几个:

    逻辑与: &&
    逻辑或: ||
    逻辑非: !
    

    逻辑运算符只能作用于布尔值,否则将产生错误。

     

    内建函数

    FreeMarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟“?”“?”后紧跟内建函数,就可以通过内建函数来轮换输出变量.下面是常用的内建的字符串函数:

    html:对字符串进行HTML编码
    cap_first:使字符串第一个字母大写
    lower_case:将字符串转换成小写
    upper_case:将字符串转换成大写
    trim:去掉字符串前后的空白字符
    

    下面是集合的常用内建函数

    size:获取序列中元素的个数
    

    下面是数字值的常用内建函数

    int:取得数字的整数部分,结果带符号
    

    例如:

    
    ${test?html}
    ${test?upper_case?html}
    

    结果是:

    Tom & Jerry   TOM & JERRY

     

    空值处理运算符

    FreeMarker对空值的处理非常严格,FreeMarker的变量必须有值,没有被赋值的变量就会抛出异常,因为FreeMarker未赋值的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误.这里所说的空值,实际上也包括那些并不存在的变量,对于一个Javanull值而言,我们认为这个变量是存在的,只是它的值为null,但对于FreeMarker模板而言,它无法理解null值,null值和不存在的变量完全相同.

    为了处理缺失变量,FreeMarker提供了两个运算符:

    ! :指定缺失变量的默认值
    ??:判断某个变量是否存在
    

    其中,!运算符的用法有如下两种:variable!variable!defaultValue,第一种用法不给缺失的变量指定默认值,表明默认值是空字符串,长度为0的集合,或者长度为0Map对象。使用!指定默认值时,并不要求默认值的类型和变量类型相同.使用??运算符非常简单,它总是返回一个布尔值,用法为:variable??,如果该变量存在,返回true,否则返回false

     

    运算符的优先级

    FreeMarker中的运算符优先级如下(由高到低排列):

    一元运算符: !
    内建函数: ?
    乘除法: *, / , %
    加减法: - , +
    比较: > , < , >= , 
        老年人
    40)>
        中年人
    20)>
        青年人
     
        少年人
    
    

    输出结果是:

    青年人
    

    上面的代码中的逻辑表达式用括号括起来主要是因为里面有>符号,由于FreeMarker会将>符号当成标签的结束字符,可能导致程序出错,为了避免这种情况,我们应该在凡是出现这些符号的地方都使用括号。

     

    switch , case , default , break指令

    这些指令显然是分支指令,作用类似于Javaswitch语句,switch指令的语法结构如下:

    
        ...
        ...
        ...
    
     

    list, break指令

    list指令是一个迭代输出指令,用于迭代输出数据模型中的集合,list指令的语法格式如下:

    
        ...
    
    

    上面的语法格式中,sequence就是一个集合对象,也可以是一个表达式,但该表达式将返回一个集合对象,而item是一个任意的名字,就是被迭代输出的集合元素.此外,迭代集合对象时,还包含两个特殊的循环变量:

    item_index:当前变量的索引值
    item_has_next:是否存在下一个对象
    

    除此之外,也可以使用指令跳出迭代。

    例子如下:

    
        ${x_index + 1}.${x},
        
    
     

    include指令

    include指令的作用类似于JSP的包含指令,用于包含指定页.include指令的语法格式如下:

    
    

    在上面的语法格式中,两个参数的解释如下:

    filename:该参数指定被包含的模板文件
    options:该参数可以省略,指定包含时的选项,包含encoding和parse两个选项,其中encoding指定包含页面时所用的解码集,而parse指定被包含文件是否作为FTL文件来解析,如果省略了parse选项值,则该选项默认是true.
    
     

    import指令

    import指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中,import指令的语法格式如下:

    
    

    上面的代码将导入/lib/common.ftl模板文件中的所有变量,交将这些变量放置在一个名为comMap对象中.

     

    noparse指令

    noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下:

    ...
    

    看如下的例子:

    
    
       ${book.name}作者:${book.author}
    
    
    

    输出如下:

    
       ${book.name}作者:${book.author}
    
    
     

    escape , noescape指令

    escape指令导致body区的插值都会被自动加上escape表达式,但不会影响字符串内的插值,只会影响到body内出现的插值,使用escape指令的语法格式如下:

    ...
        ...
    
    

    看如下的代码:

    
        First name:${firstName}
        Last name:${lastName}
        Maiden name:${maidenName}
    
    

    上面的代码等同于:

    First name:${firstName?html}
    Last name:${lastName?html}
    Maiden name:${maidenName?html}
    

    escape指令在解析模板时起作用而不是在运行时起作用,除此之外,escape指令也嵌套使用,子escape继承父escape的规则,如下例子:

    
        Customer Name:${customerName}
        Items to ship;
        
           ${itemCode1}
           ${itemCode2}
           ${itemCode3}
           ${itemCode4}
        
    
    

    上面的代码类似于:

    Customer Name:${customerName?html}
    Items to ship;
    ${itemCodeToNameMap[itemCode1]?html}
    ${itemCodeToNameMap[itemCode2]?html}
    ${itemCodeToNameMap[itemCode3]?html}
    ${itemCodeToNameMap[itemCode4]?html}
    

    对于放在escape指令中所有的插值而言,这此插值将被自动加上escape表达式,如果需要指定escape指令中某些插值无需添加escape表达式,则应该使用noescape指令,放在noescape指令中的插值将不会添加escape表达式。

     

    assign指令

    assign指令在前面已经使用了多次,它用于为该模板页面创建或替换一个顶层变量,assign指令的用法有多种,包含创建或替换一个顶层变量,或者创建或替换多个变量等,它的最简单的语法如下:

    
    

    这个用法用于指定一个名为name的变量,该变量的值为value,此外,FreeMarker允许在使用assign指令里增加in子句,in子句用于将创建的name变量放入namespacehash命名空间中。

    assign指令还有如下用法:

    
    

    这个语法可以同时创建或替换多个顶层变量,此外,还有一种复杂的用法,如果需要创建或替换的变量值是一个复杂的表达式,则可以使用如下语法格式:

    capture this
    

    在这个语法中,是指将assign指令的内容赋值给name变量。如下例子:

    
        
            ${n}
        
    
    ${x}
    

    上面的代码将产生如下输出:

    星期一 星期二 星期三 星期四 星期五 星期六 星期天
    

    虽然assign指定了这种复杂变量值的用法,但是我们也不要滥用这种用法,如下例子:

    Hello ${user}!
    

    以上代码改为如下写法更合适:

     

    setting指令

    该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:,在这个格式中,name的取值范围包含如下几个:

    locale:该选项指定该模板所用的国家/语言选项
    number_format:指定格式化输出数字的格式
    boolean_format:指定两个布尔值的语法格式,默认值是true,false
    date_format,time_format,datetime_format:指定格式化输出日期的格式
    time_zone:设置格式化输出日期时所使用的时区
     

    macro , nested , return指令

    macro可以用于实现自定义指令,通过使用自定义指令,可以将一段模板片段定义成一个用户指令,使用macro指令的语法格式如下:

    
        ...
        
        ...
        
        ...
    
    

    在上面的格式片段中,包含了如下几个部分:

    name:name属性指定的是该自定义指令的名字,使用自定义指令时可以传入多个参数
    paramX:该属性就是指定使用自定义指令时报参数,使用该自定义指令时,必须为这些参数传入值
    nested指令:nested标签输出使用自定义指令时的中间部分
    nested指令中的循环变量:这此循环变量将由macro定义部分指定,传给使用标签的模板
    return指令:该指令可用于随时结束该自定义指令.
    

    看如下的例子:

       //定义一个自定义指令
    j2ee
    
        //使用刚才定义的指令
    

    上面的代码输出结果为:

    j2ee
    

    在上面的代码中,可能很难看出自定义标签的用处,因为我们定义的book指令所包含的内容非常简单,实际上,自定义标签可包含非常多的内容,从而可以实现更好的代码复用.此外,还可以在定义自定义指令时,为自定义指令指定参数,看如下代码:

         //定义一个自定义指令booklist是参数
        
           ${book}
        
    
       //使用刚刚定义的指令
    

    上面的代码为book指令传入了一个参数值,上面的代码的输出结果为:

    spring j2ee
    

    不仅如此,还可以在自定义指令时使用nested指令来输出自定义指令的中间部分,看如下例子:

    
    
    
       FreeMarker示例页面 - ${title?html}
    
    
       ${title?html}
             //用于引入用户自定义指令的标签体
    
    
    
    

    上面的代码将一个HTML页面模板定义成一个page指令,则可以在其他页面中如此page指令:

         //假设上面的模板页面名为common.ftl,导入页面
    
    
    
  • spring
  • j2ee
  • 从上面的例子可以看出,使用macronested指令可以非常容易地实现页面装饰效果,此外,还可以在使用nested指令时,指定一个或多个循环变量,看如下代码:

    
          //使用book指令时指定了一个循环变量值
    
    
     ${x} .图书
    

    当使用nested指令传入变量值时,在使用该自定义指令时,就需要使用一个占位符(如book指令后的;x).上面的代码输出文本如下:

    1 .图书    2 .图书
    

    nested指令中使用循环变量时,可以使用多个循环变量,看如下代码:

    
             //使用nested指令时指定了三个循环变量
           
        
    
    
        ${c}. ${halfc} Last! 
    
    

    上面的输出结果为:

    1. 0.5   2. 1   3. 1.5   4. 2 Last;
    

    return指令用于结束macro指令,一旦在macro指令中执行了return指令,则FreeMarker不会继续处理macro指令里的内容,看如下代码:

    
    spring
    
    j2ee
    
    
    

    上面的代码输出:

    spring
    

    j2ee位于return指令之后,不会输出。

     

    t, lt, rt 指令

    语法:

     去掉左右空白和回车换行
     去掉左边空白和回车换行
     去掉右边空白和回车换行
     取消上面的效果

     

    高级方法

    自定义方法

    自定义方法, 如:

    ${timer("yyyy-MM-dd H:mm:ss", x)}
    ${timer("yyyy-MM-dd ", x)}
    

    在模板中除了可以通过对象来调用方法外(${object.methed(args)})也可以直接调用java实现的方法,java类必须实现接口TemplateMethodModel的方法exec(List args). 下面以把毫秒的时间转换成按格式输出的时间为例子:

    public class LongToDate implements TemplateMethodModel {
        
        public TemplateModel exec(List args) throws TemplateModelException {
            SimpleDateFormat mydate = new SimpleDateFormat((String) args.get(0)));
            return mydate.format(new Date(Long.parseLong((String)args.get(1)));
        }
    }  
    

    LongToDate对象放入到数据模型中:

        root.put("timer", new LongToDate());
    

    ftl模板里使用:

    
    ${timer("yyyy-MM-dd H:mm:ss", x)}
    ${timer("yyyy-MM-dd ", x)}
    

    输出结果:

    2001-10-12 5:21:12
    2001-10-12

     

    自定义 Transforms

    实现自定义的文本或表达式的功能,允许对中间的最终文本进行解析转换

    例子:实现strstr转换成STR的功能.

    代码如下:

    import java.io.*;
    import java.util.*;
    import freemarker.template.TemplateTransformModel;
    public class UpperCaseTransform implements TemplateTransformModel {
        public Writer getWriter(Writer out, Map args) {
            return new UpperCaseWriter(out);
        }
        private class UpperCaseWriter extends Writer {
           
            private Writer out;
               
            UpperCaseWriter (Writer out) {
                this.out = out;
            }
            public void write(char[] cbuf, int off, int len)
                    throws IOException {
                out.write(new String(cbuf, off, len).toUpperCase());
            }
            public void flush() throws IOException {
                out.flush();
            }
            public void close() {
            }
        }
    }
    

    然后将此对象put到数据模型中, 如下所示:

    root.put("upcase", new UpperCaseTransform());
    

    view(ftl)页面中可以如下方式使用:

    
    hello world
    
    

    打印输出:

    HELLO WORLD

     

    关注
    打赏
    1665965058
    查看更多评论
    立即登录/注册

    微信扫码登录

    0.0666s