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

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文件中添加:

<dependency>
  <groupId>org.freemarker</groupId>
  <artifactId>freemarker</artifactId>
  <version>2.3.28</version>
</dependency>

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

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>${spring.version}</version>
</dependency>

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

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

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--配置前端控制器-->
    <servlet>
        <servlet-name>spring-mvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/springmvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-mvc</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <!--配置编码格式为utf-8-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
    <context:component-scan base-package="com.itszt.controller"></context:component-scan>
 
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
 
    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="defaultEncoding" value="UTF-8"></property>
        <property name="templateLoaderPath" value="/WEB-INF/templates/"></property>
    </bean>
    <!--spring-mvc整合freemarker-->
    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"></property>
        <property name="contentType" value="text/html;charset=UTF-8"></property>
        <property name="suffix" value=".ftl"></property>
    </bean>
</beans>

网站首页如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
    <a href="/testController/test1.html">test1</a>
    <hr>
  <a href="/testController/test2.html">test2</a>
    <hr>
    <a href="/testController/test3.html">test3</a>
    <hr>
    <a href="/testController/test4.html">test4</a>
    <hr>
    <a href="/testController/test5.html">test5</a>
    <hr>
    <a href="/testController/test6.html">test6</a>
    <hr>
    <a href="/testController/test7.html">test7</a>
    <hr>
    <a href="/testController/test8.html">test8</a>
    <hr>
    <a href="/testController/test9.html">test9</a>
  </body>
</html>

 后台Java代码如下:

import java.util.List;
 
/**
 * 实体类
 */
public class User {
    private String username,usersex;
    private int userage;
    private String nation,hometown;
    private List<String> 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<String> getUserhbs() {
        return userhbs;
    }
 
    public void setUserhbs(List<String> 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<String> 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<String> list=new ArrayList<>();
        list.add("爬山");
        list.add("游泳");
        list.add("打篮球");
        model.addAttribute("data1",list);
 
        Map<String,String> 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<User> 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","<h3 stype='color:red;font-weight:bold;'>你好,霍金先生,请一路走好!</h3>");
        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
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我是第一个FreeMarker
<hr>
动态数据展示1:${data1}
<hr>
动态数据展示2:${data2}
<hr>
动态数据展示2:${data3}
</body>
</html>
-----------------------------------------
test2.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示javabean和日期
<hr>
动态数据展示1:${data1.username}--${data1.usersex}--${data1.userage}--${data1.nation}--${data1.hometown}
<hr>
动态数据展示2:${data2?string("yyyy-MM-dd HH:mm:ss")}
 
</body>
</html>
----------------------------------------------
test3.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示javabean和日期
<hr>
动态数据展示1:
该集合中元素的个数:${data1?size} 个
<#list data1 as hb>
    <h3>
        ${hb_index}.${hb}
    </h3>
</#list>
<hr>
动态数据展示2:
该集合中元素的个数:${data2?size} 个
<#list data2?keys as mapKey>
   <h3>
   ${mapKey}--${data2[mapKey]}
   </h3>
</#list>
</body>
</html>
---------------------------------------------
test4.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示date和javabean的FreeMarker
<hr>
动态数据展示:
该集合中共有 : ${users?size}  个
<#list users as user >
    <h4>${user_index+1}. ${user.username}  ${user.usersex} ${user.userage}</h4>
</#list>
</body>
</html>
---------------------------------------------------
test5.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示date和javabean的FreeMarker
<hr>
<h3>演示字符串拼接</h3>
${"前面的东西${str1}"}
<br>
${"${str2}后面的东西"}
<br>
${intData1}
</body>
</html>
-------------------------------------------------
test6.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示date和javabean的FreeMarker
<hr>
<#if scoreMath gt 90>
    数学优秀
<#elseif scoreMath gte 60>
数学一般
<#else>
数学不及格
</#if>
<hr>
<#if (scoreMath+scoreJava) gt 160>
    优秀学生
<#elseif (scoreMath+scoreJava) gte 120>
一般学生
<#else >
不及格学生
</#if>
</body>
</html>
------------------------------------------------
test7.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
你好啊。。我来显示date和javabean的FreeMarker
<hr>
${htmlData}
<hr>
${htmlData?html}
<hr>
${upperData?upper_case}
<hr>
${lowerData?lower_case}
<hr>
${cfData?cap_first}
<hr>
${intData?int}
</body>
</html>
----------------------------------------
test8_1.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<hr>
我是8-1的东西。。。。嘻嘻哈哈
<#include "test8_2.ftl">
</body>
</html>
---------------------------------------
test8_2.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<hr>
我是8-2的东西。。。。
</body>
</html>
------------------------------------------
test9.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<hr>
演示macro自定义标签:
<#import "test10.ftl" as xgk />
1
<#--<@xgk.tag1 data="${data1}">xxxxxxxxx</@xgk.tag1>-->
 
<@xgk.tag1 data="${data1}">xxxxxxxxx</@xgk.tag1>
 
<hr><hr>
</body>
</html>
-----------------------------------------
test10.ftl
<html>
<head>
    <title>$Title$</title>
</head>
<body>
<hr>
<#--<#macro tag1 data>
    <hr>
    <hr>
    <h3 style="color: red">我是一个自定义的标签的效果,展示后台传来的数据:</h3>
    <h5>${data} <#nested /></h5>
    <hr>
    <hr>
</#macro>-->
 
<#macro tag1 data>
<hr>
<hr>
<h3 style="color: red;">
    我是test10.ftl
</h3>
<h5>
    ${data} <#nested />
</h5>
<hr>
<hr>
</#macro>
</body>
</html>

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

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

 

FTL指令

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

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

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

<html>
    <head>
        <title>Welcome!</title>
    </head>
    <body>
        <#-- 注释部分 -->
        <#-- 下面使用插值 -->
        <h1>Welcome ${user} !</h1>
        <p>We have these animals:</p>
        <u1>
            <#-- 使用FTL指令 -->
            <#list animals as being>
            <li>${being.name} for ${being.price} Euros</li>
            <#list>
        <u1>
    </body>
</html>

FTL指令规则

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

开始标签:<#directivename parameter>
结束标签:</#directivename>
空标签:<#directivename parameter/>

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

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

插值规则

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

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

通用插值

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

1.插值结果为字符串值:直接输出表达式结果
2.插值结果为数字值:根据默认格式(由#setting指令设置)将表达式结果转换成文本输出.可以使用内建的字符串   函数格式化单个插值,如下面的例子:
<#settion number_format="currency"/>
<#assign answer=42/>
${answer}
${answer?string} <#-- the same as ${answer} -->
${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指令设置)将表达式结果转换成文本输出.可以使用内建的字符串函数格式化单个插值,如下面的例子:
<#assign foo=true/>
${foo?string("yes", "no")}

输出结果是:

yes

数字格式化插值

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

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

如下面的例子:

<#assign x=2.582/>
<#assign y=4/>
#{x; M2} <#-- 输出2.58 -->
#{y; M2} <#-- 输出4 -->
#{x; m2} <#-- 输出2.6 -->
#{y; m2} <#-- 输出4.0 -->
#{x; m1M2} <#-- 输出2.58 -->
#{x; m1M2} <#-- 输出4.0 -->

 

表达式

表达式是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,不使用引号.

集合, 集合以方括号包括,各集合元素之间以英文逗号","分隔

如下为集合元素遍历的例子:

<#list ["星期一", "星期二", "星期三", "星期四", "星期五",     "星期六", "星期天"] as x>
    ${x}
</#list>

输出结果是:

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

除此之外,集合元素也可以是表达式,例子如下:

[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!,可以看出这两种语法的效果完全一样.
值得注意的是,${..}只能用于文本部分,不能用于表达式,下面的代码是错误的:

<#if ${isBig}>Wow!</#if>
<#if "${isBig}">Wow!</#if>

应该写成:

<#if isBig>Wow!</#if>

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

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

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

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

 

集合连接运算符

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

<#list ["星期一","星期二","星期三"] + ["星期四","星期五","星期六","星期天"] as x>
    ${x}
</#list>

输出结果是:

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

 

Map连接运算符

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

<#assign scores = {"语文":86,"数学":78} + {"数学":87,"Java":93}>
语文成绩是${scores.语文}
数学成绩是${scores.数学}
Java成绩是${scores.Java}

输出结果是:

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

 

算术运算符

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

<#assign x=5>
${ x * x - 100 }
${ x /2 }
${ 12 %10 }

输出结果是:

-75   2.5   2

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

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

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

<#assign x=5>
${ (x/2)?int }
${ 1.1?int }
${ 1.999?int }
${ -1.1?int }
${ -1.999?int }

结果是:

2 1 1 -1 -1

 

比较运算符

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

=或者==:判断两个值是否相等.
!=:判断两个值是否不等.
>或者gt:判断左边值是否大于右边值
>=或者gte:判断左边值是否大于等于右边值
<或者lt:判断左边值是否小于右边值
<=或者lte:判断左边值是否小于等于右边值

注意:=和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且FreeMarker是精确比较,"x","x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时候,使用gt等字母运算符代替>会有更好的效果,因为FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:<#if (x>y)>。

 

逻辑运算符

逻辑运算符有如下几个:

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

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

 

内建函数

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

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

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

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

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

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

例如:

<#assign test="Tom & Jerry">
${test?html}
${test?upper_case?html}

结果是:

Tom &amp; Jerry   TOM &amp; JERRY

 

空值处理运算符

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

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

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

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

 

运算符的优先级

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

一元运算符: !
内建函数: ?
乘除法: *, / , %
加减法: - , +
比较: > , < , >= , <= (lt , lte , gt , gte)
相等: == , = , !=
逻辑与: &&
逻辑或: ||
数字范围: ..

实际上,我们在开发过程中应该使用括号来严格区分,这样的可读性好,出错少。

 

if指令

这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下:

<#if condition>...
<#elseif condition>...
<#elseif condition>...
<#else> ...
</#if>

例子如下:

<#assign age=23>
<#if (age>60)>
    老年人
<#elseif (age>40)>
    中年人
<#elseif (age>20)>
    青年人
<#else> 
    少年人
</#if>

输出结果是:

青年人

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

 

switch , case , default , break指令

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

<#switch value>
    <#case refValue>...<#break>
    <#case refValue>...<#break>
    <#default>...
</#switch>

 

list, break指令

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

<#list sequence as item>
    ...
</#list>

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

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

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

例子如下:

<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x>
    ${x_index + 1}.${x}<#if x_has_next>,</if>
    <#if x="星期四"><#break></#if>
</#list>

 

include指令

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

<#include filename [options]>

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

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

 

import指令

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

<#import "/lib/common.ftl" as com>

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

 

noparse指令

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

<#noparse>...</#noparse>

看如下的例子:

<#noparse>
<#list books as book>
   <tr><td>${book.name}<td>作者:${book.author}
</#list>
</#noparse>

输出如下:

<#list books as book>
   <tr><td>${book.name}<td>作者:${book.author}
</#list>

 

escape , noescape指令

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

<#escape identifier as expression>...
    <#noescape>...</#noescape>
</#escape>

看如下的代码:

<#escape x as x?html>
    First name:${firstName}
    Last name:${lastName}
    Maiden name:${maidenName}
</#escape>

上面的代码等同于:

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

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

<#escape x as x?html>
    Customer Name:${customerName}
    Items to ship;
    <#escape x as itemCodeToNameMap[x]>
       ${itemCode1}
       ${itemCode2}
       ${itemCode3}
       ${itemCode4}
    </#escape>
</#escape>

上面的代码类似于:

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指令的用法有多种,包含创建或替换一个顶层变量,或者创建或替换多个变量等,它的最简单的语法如下:

<#assign name=value [in namespacehash]>

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

assign指令还有如下用法:

<#assign name1=value1 name2=value2 ... nameN=valueN [in namespacehash]>

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

<#assign name [in namespacehash]>capture this</#assign>

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

<#assign x>
    <#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as n>
        ${n}
    </#list>
</#assign>
${x}

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

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

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

<#assign x>Hello ${user}!</#assign>

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

<#assign x="Hello ${user}!">

 

setting指令

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

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

 

macro , nested , return指令

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

<#macro name param1 param2 ... paramN>
    ...
    <#nested loopvar1, loopvar2, ..., loopvarN>
    ...
    <#return>
    ...
</#macro>

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

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

看如下的例子:

<#macro book>   //定义一个自定义指令
j2ee
</#macro>
<@book />    //使用刚才定义的指令

上面的代码输出结果为:

j2ee

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

<#macro book booklist>     //定义一个自定义指令booklist是参数
    <#list booklist as book>
       ${book}
    </#list>
</#macro>
<@book booklist=["spring","j2ee"] />   //使用刚刚定义的指令

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

spring j2ee

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

<#macro page title>
<html>
<head>
   <title>FreeMarker示例页面 - ${title?html}</title>
</head>
<body>
   <h1>${title?html}</h1>
   <#nested>      //用于引入用户自定义指令的标签体
</body>
</html>
</#macro>

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

<#import "/common.ftl" as com>     //假设上面的模板页面名为common.ftl,导入页面
<@com.page title="book list">
<u1>
<li>spring</li>
<li>j2ee</li>
</ul>
</@com.page >

从上面的例子可以看出,使用macronested指令可以非常容易地实现页面装饰效果,此外,还可以在使用nested指令时,指定一个或多个循环变量,看如下代码:

<#macro book>
<#nested 1>      //使用book指令时指定了一个循环变量值
<#nested 2>
</#macro>
<@book ;x> ${x} .图书</@book >

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

1 .图书    2 .图书

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

<#macro repeat count>
    <#list 1..count as x>     //使用nested指令时指定了三个循环变量
       <#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;

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

<#macro book>
spring
<#return>
j2ee
</#macro>
<@book />

上面的代码输出:

spring

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

 

t, lt, rt 指令

语法:

<#t> 去掉左右空白和回车换行
<#lt> 去掉左边空白和回车换行
<#rt> 去掉右边空白和回车换行
<#nt> 取消上面的效果

 

高级方法

自定义方法

自定义方法, 如:

${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模板里使用:

<#assign x = "123112455445">
${timer("yyyy-MM-dd H:mm:ss", x)}
${timer("yyyy-MM-dd ", x)}

输出结果:

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

 

自定义 Transforms

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

例子:实现<@upcase>str</@upcase >str转换成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)页面中可以如下方式使用:

<@upcase>
hello world
</@upcase >

打印输出:

HELLO WORLD


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wespten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值