文章目录
什么是FreeMarker
FreeMarker 是一款模板引擎:即一种基于模板、用来生成输出文本(任何来自于 HTML格式的文本用来自动生成源代码、电子邮件、配置文件、源代码等)的通用工具 (可用于进行页面静态化的模板) 。它是为 Java 程序员提供的一个开发包,或者说是一个类库。它不是面向最终用户的,而是为程序员提供的一款可以嵌入他们所开发产品的应用程序。
FreeMarker 实际上是被设计用来生成 HTML 页面,尤其是通过实现了基于 MVC(Model View Controller,模型-视图-控制器)模式的 Java Servlet 应用程序。使用 MVC 模式的动态页面 的设计构思使得你可以将前端设计师(编写 HTML 页面的人员)从程序员中分离出来。那么,所有人各司其职,发挥其最擅长的一面。网页设计师可以改写页面的显示效果而不受程序员编译代码的影响,因为应用程序的逻辑(这里是 Java 程序)和页面设计(这里是 FreeMarker 模 板)已经被分开了。页面模板代码不会受到复杂程序代码的影响。这种分离的思想即便对一个程序员和页面设计师是同一个人的项目来说也都是非常有用的,因为分离使得代码保持简洁而且易于维护。
尽管 FreeMarker 也拥有一些编程能力,但是它却不像 PHP 那样,是的一种全面的编程语言。反而,Java 程序准备的数据来进行显示(比如 SQL 数据库查询),FreeMarker 仅仅是使用模板生成文本页面来呈现已经准备好的数据而已。
FreeMarker 不是 Web 开发的应用程序框架。它是一个适用于 Web 应用程序框架中的组件,但是 FreeMarker 引擎本身并不知道 HTTP 协议或 Java Servlet 的存在。它仅仅来生成文本内容。既然是这样,它也非常适用于非 Web 应用程序的开发环境。只是要注意的是,我们使用 FreeMarker 作为视图层的组件,是为了给诸如 Struts 这样的 Model 2 应用框架提供现成的解决方案。
FreeMarker 本身是免费的,它基于 BSD 规则的许可协议。它也是 OSI 认证的开源软件。OSI 认证是开源倡议的认证标识。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据,而在模板之外注意于要展示什么数据。
常用的java模板引擎还有哪些?
Jsp、Freemarker、Thymeleaf 、Velocity 等。
一、模板开发入门
1、模板+数据模型=输出
freemarker并不关心数据的来源,只是根据模板的内容,将数据模型在模板中显示并输出文件(通常为html,也 可以生成其它格式的文本文件):
1、数据模型 数据模型在java中可以是基本类型也可以List、Map、Pojo等复杂类型。
2、来自官方的例子:https://freemarker.apache.org/docs/dgui_quickstart_basics.html
数据模型:
模板:
输出:
2、FreeMarker快速入门
freemarker作为springmvc一种视图格式,默认情况下SpringMVC支持freemarker视图格式。 需要创建Spring Boot+Freemarker工程用于测试模板。
1)、创建测试工程
创建一个freemarker 的测试工程专门用于freemarker的功能测试与模板的测试。
测试环境搭建目录:
pom.xml如下源码:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>xc-framework-parent</artifactId>
<groupId>com.xuecheng</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../xc-framework-parent/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>test-freemarker</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>
2)、配置文件
配置application.yml和 logback-spring.xml。
application.yml内容如下:
server:
port: 8088 #服务端口
spring:
application:
name: test-freemarker #指定服务名
freemarker:
cache: false #关闭模板缓存,方便测试
settings:
template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
logback-spring.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--定义日志文件的存储地址,使用绝对路径-->
<property name="LOG_HOME" value="d:/logs"/>
<!-- Console 输出设置 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<fileNamePattern>${LOG_HOME}/xc.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 异步输出 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="FILE"/>
</appender>
<logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="org.springframework.boot" level="DEBUG"/>
<root level="info">
<!--<appender-ref ref="ASYNC"/>-->
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
3)、创建模型类
在freemarker的测试工程下创建模型类型用于测试:
Student.java内容如下:
import lombok.Data;
import lombok.ToString;
import java.util.Date;
import java.util.List;
@Data
@ToString
public class Student {
private String name;//姓名
private int age;//年龄
private Date birthday;//生日
private Float mondy;//钱包
private List<Student> friends;//朋友列表
private Student bestFriend;//最好的朋友
}
4)、创建模板
在 src/main/resources下创建templates,此目录为freemarker的默认模板存放目录。
在templates下创建模板文件test1.ftl,模板中的${name}最终会被freemarker替换成具体的数据。
test1.ftl内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
Hello,${name}!
</body>
</html>
5)、创建controller
创建Controller类,向Map中添加name,最后返回模板文件。
FreemarkerController.java内容如下:
@RequestMapping("/freemarker")
@Controller //这里不要使用@RestController,要输出html网页,testController输出时json数据
public class FreemarkerController {
//测试1
@RequestMapping("/test1")
public String test1(Map<String, Object> map) {
//map就是freemarker模板所使用的数据
map.put("name", "小懒猪");
//返回freemarker模板的位置,基于resources/templates路径的
return "test1";
}
}
6)、创建启动类
FreemarkerTestApplication.java:
@SpringBootApplication
public class FreemarkerTestApplication {
public static void main(String[] args) {
SpringApplication.run(FreemarkerTestApplication.class,args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
测试结果:
以上便是freemarker的环境搭建ヾ(◍°∇°◍)ノ゙
二、FreeMarker基础
1、基础语法种类
1、注释,即<#‐‐和‐‐>,介于其之间的内容会被freemarker忽略 。
2、插值(Interpolation):即 ${..}部分,freemarker会用真实的值代替${..} 。
3、FTL指令:和HTML标记类似,名字前加#予以区分,Freemarker会解析标签中的表达式或逻辑。
4、文本,仅文本信息,这些不是freemarker的注释、插值、FTL指令的内容会被freemarker忽略解析,直接输出内容。
下边方法形参map即为freemarker静态化所需要的数据模型,在map中填充数据:
FreemarkerController.java:
@RequestMapping("/test1")
public String test1(Map<String, Object> map) {
//向数据模型放数据
map.put("name","小宝贝");
Student stu1 = new Student();
stu1.setName("小懒猪");
stu1.setAge(21);
stu1.setMondy(1314.2f);
stu1.setBirthday(new Date());
Student stu2 = new Student();
stu2.setName("猪头");
stu2.setMondy(520.0f);
stu2.setAge(20);
stu2.setBirthday(new Date());
List<Student> friends = new ArrayList<>();
friends.add(stu1);
stu2.setFriends(friends);
stu2.setBestFriend(stu1);
List<Student> stus = new ArrayList<>();
stus.add(stu1); stus.add(stu2);
//向数据模型放数据
map.put("stus",stus);
//准备map数据
HashMap<String,Student> stuMap = new HashMap<>();
stuMap.put("stu1",stu1);
stuMap.put("stu2",stu2);
//向数据模型放数据
map.put("stu1",stu1);
//向数据模型放数据
map.put("stuMap",stuMap);
//返回模板文件名称
return "test1";
}
2、list指令
在test1.ftl模板中使用list指令遍历数据模型中的数据:
test1.ftl:
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#list stus as stu>
<tr>
<td>${stu_index + 1}</td> <#--说明: _index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始-->
<td <#if stu.name =='小明'>style="background:red;"</#if>>${stu.name}</td>
<td>${stu.age}</td>
<td >${stu.mondy}</td>
</tr>
</#list>
</table>
测试结果:
3、遍历map数据
在test1.ftl模板中使用map指令遍历数据模型中的stuMap,
test1.ftl:
遍历数据模型中的stuMap(map数据),第一种方法:在中括号中填写map的key,第二种方法:在map后边直接加"点(.) key"
<br/>
输出stu1的学生信息:<br/>
姓名:${stuMap['stu1'].name}<br/>
年龄:${stuMap['stu1'].age}<br/>
输出stu1的学生信息:<br/>
姓名:${stu1.name}<br/>
年龄:${stu1.age}<br/>
遍历map中的key.stuMap?keys就是列表(是一个list)<br/>
遍历输出两个学生信息:<br/>
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#list stuMap?keys as k>
<tr>
<td>${k_index + 1}</td>
<td>${stuMap[k].name}</td>
<td>${stuMap[k].age}</td>
<td >${stuMap[k].mondy}</td>
</tr>
</#list>
</table>
测试结果:
4、if指令
在test1.ftl模板中使用if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否则跳过内容不再输出。
test1.ftl:
<table>
<tr>
<td>序号</td>
<td>姓名</td>
<td>年龄</td>
<td>钱包</td>
</tr>
<#list stus as stu>
<tr>
<td>${stu_index + 1}</td> <#--说明: _index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始-->
<td <#if stu.name =='小懒猪'>style="background:red;"</#if>>${stu.name}</td>
<td <#if stu.age gt 20>style="background:yellow;"</#if>>${stu.age}</td>
<td <#if (stu.mondy > 999)>style="background:blue;"</#if>>${stu.mondy}</td>
</tr>
</#list>
</table>
测试结果:
对于在if里运用的运算符,参考如下:
1、算数运算符 :
FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %
2、逻辑运算符 :
逻辑运算符有如下几个:
逻辑与:&&
逻辑或:||
逻辑非:!
逻辑运算符只能作用于布尔值,否则将产生错误
3、 比较运算符 :
表达式中支持的比较运算符有如下几个:
1、 =或者==:判断两个值是否相等.
2、 !=:判断两个值是否不等.
3、 > 或者gt:判断左边值是否大于右边值
4、 >=或者gte:判断左边值是否大于等于右边值
5 、<或者lt:判断左边值是否小于右 边值
6 、<=或者lte:判断左边值是否小于等于右边值
注意: =和!=可以用于字符串,数值和日期来比较是否相等,但=和!=两边必须是相同类型的值,否则会产生错误,而且 FreeMarker是精确比较,“x”,"x ","X"是不等的.其它的运行符可以作用于数字和日期,但不能作用于字符串,大部分的时 候,使用gt等字母运算符代替>会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括 号来避免这种情况,如:<#if (x>y)>
5、空值处理
1、判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false
例如先把FreemarkerController中的:
//向数据模型放数据
//map.put("stus",stus);
注释掉,运行访问会出现什么情况呢?
是的,报错了,因为没有数据的原因出现了这个错误,而现在就是对空值应该怎么处理呢?
可以写入 <#if stus??></#if>
<#if stus??>
<#list stus as stu>
<tr>
<td>${stu_index + 1}</td> <#--说明: _index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始-->
<td <#if stu.name =='小懒猪'>style="background:red;"</#if>>${stu.name}</td>
<td <#if stu.age gt 20>style="background:yellow;"</#if>>${stu.age}</td>
<td <#if (stu.mondy > 999)>style="background:blue;"</#if>>${stu.mondy}</td>
</tr>
</#list>
</#if>
运行结果:
2、缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。 例: ${name!’’}表示如果name为空显示空字符串。 如果是嵌套对象则建议使用()括起来。
例: ${(stu1.name)!’’}表示,如果stu1或name为空默认显示空字符串。
都先注释掉:
// stuMap.put(“stu1”,stu1);
// map.put(“stu1”,stu1);
输出stu1的学生信息:<br/>
姓名:${(stuMap['stu1'].name)!''}<br/>
年龄:${(stuMap['stu1'].age)!''}<br/>
输出stu1的学生信息:<br/>
姓名:${(stu1.name)!''}<br/>
年龄:${(stu1.age)!''}<br/>
运行结果:
6、内建函数
内建函数语法格式: 变量+?+函数名称
1、和到某个集合的大小
${集合名?size}
例:学生个数:${stus?size}
2、日期格式化
显示年月日: ${today?date}
显示时分秒:${today?time}
显示日期+时间:${today?datetime} <br>
自定义格式化: ${today?string("yyyy年MM月")}
例:
<td>${stu.birthday?date}</td>
<td>${stu.birthday?time}</td>
<td>${stu.birthday?datetime}</td>
<td>${stu.birthday?string("yyyy年MM月")}</td>
运行结果:
3、内建函数c
map.put("point", 102920122);
运行结果:
point是数字型,使用 ${point} 会显示这个数字的值,不并每三位使用逗号分隔。
如果不想显示为每三位分隔的数字,可以使用c函数将数字型转成字符串输出
${point?c}
运行结果:
4、将json字符串转成对象一个例子:
其中用到了assign标签,assign的作用是定义一个变量。
<#assign text="{'bank':'工商银行','account':'10101920201920212'}" />
<#assign data=text?eval />
开户行:${data.bank} 账号:${data.account}
运行结果:
以上便是有关FreeMarker比较常用的基础指令,若想深入了解FreeMarker,可以查看这本书FreeMarker_Manual_zh_CN:
链接: https://pan.baidu.com/s/1OsCpIqaALy73__5rAVHPBA
提取码: by5h
至此便是本文章关于FreeMarker简单的基础入门<(▰˘◡˘▰)