FreeMarker是一款用java语言编写的模版引擎,它虽然不是web应用框架,但它很合适作为web应用框架的一个组件。
特点:
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>bootstrapartifactId>
<version>3.3.5version>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>${testng.version}version>
<scope>testscope>
dependency>
dependencies>
配置文件:
server:
port: 8095
spring:
freemarker:
template-loader-path: ["classpath:/templates/"] # 设置ftl文件路径
cache: false # 设置页面缓存
charset: UTF-8 # 设置页面编码格式
check-template-location: true
content-type: text/html # 设置文档类型
expose-request-attributes: false
expose-session-attributes: false
request-context-attribute: request #可以让Freemarker获取项目根路经
suffix: .ftl # 设置模板后缀名
# 设置静态文件路径,js,css等
mvc:
static-path-pattern: /static/**
编写index.ftl文件:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
this is index page
body>
html>
编写welcome.ftl文件:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
Hello ${name} from resource freemark!
body>
html>
编写controller
//Tips: 由于要返回模板页面文件,所以我们只能使用@Controller 而不可以使用@RestController
//@RestController
@Controller
@Slf4j
public class StuController {
@RequestMapping(value = "/")
public String index() {
return "index";
}
@RequestMapping(value = "/welcome")
public String hello1(Model m){
m.addAttribute("name", "spring-boot11");
return "welcome";
}
@RequestMapping("hello")
public ModelAndView hello(ModelAndView m){
m.addObject("name", "spring-boot");
m.setViewName("welcome");
return m;
}
}
@RequestMapping("sysUser")
public String user(Model m){
List<SysUser> list = new ArrayList<>();
SysUser u1 = new SysUser(0001, "hello1", "11111111111111111");
SysUser u2 = new SysUser(0002, "hello2", "22222222222222222");
SysUser u3 = new SysUser(0003, "hello3", "33333333333333333");
list.add(u1);
list.add(u2);
list.add(u3);
m.addAttribute("userList", list);
m.addAttribute("sysUser", "SysUser");
return "sysUser/users";
}
在src\main\resources\templates\sysuser
目录下编写users.ftl:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<meta content="text/html;charset=utf-8">meta>
<title>Hello World!title>
<#--<script src="https://code.jquery.com/jquery-3.3.1.min.js">script>-->
<script src="webjars/jquery/3.1.1/jquery.min.js">script>
<script src="webjars/bootstrap/3.3.5/js/bootstrap.min.js">script>
<link rel="stylesheet" href="webjars/bootstrap/3.3.5/css/bootstrap.min.css" />
head>
<body>
<div class="container">
<table class="table">
<caption>${sysUser}caption>
<thead>
<tr>
<th>First Nameth>
<th>Last Nameth>
<th>User Nameth>
tr>
thead>
<tbody>
<tr>
<td>aehyoktd>
<td>leotd>
<td>@aehyoktd>
tr>
<tr>
<td>lynntd>
<td>thltd>
<td>@lynntd>
tr>
<#list userList as user>
<tr>
<td>${user.id}td>
<td>${user.name}td>
<td>${user.phone}td>
tr>
#list>
tbody>
table>
div>
body>
html>
上面都是讲的返回页面使用freemarker,有时候可能会需要在代码中使用到freemarker进行模板的运算。接下来写个demo。
编写一个测试类
package com.example.service;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class FreemarkerDemo {
private static final String TEMPLATE_PATH = "springboot-freemarker/src/main/java/com/example/service";
private static final String CLASS_PATH = "springboot-freemarker/src/main/java/com/example/service";
public static void main(String[] args) {
// step1 创建freeMarker配置实例
Configuration configuration = new Configuration();
Writer out = null;
try {
// step2 获取模版路径
configuration.setDirectoryForTemplateLoading(new File(TEMPLATE_PATH));
// step3 创建数据模型
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("classPath", "com.example.service");
dataMap.put("className", "AutoCodeDemo");
dataMap.put("helloWorld", "通过简单的 <代码自动生产程序> 演示 FreeMarker的HelloWorld!");
// step4 加载模版文件
Template template = configuration.getTemplate("hello.ftl");
// step5 生成数据
File docFile = new File(CLASS_PATH + "\\" + "AutoCodeDemo.java");
if (!docFile.exists()) {
docFile.createNewFile();
}
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
// step6 输出文件
template.process(dataMap, out);
System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^AutoCodeDemo.java 文件创建成功 !");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != out) {
out.flush();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
编写hello.ftl
package ${classPath};
public class ${className} {
public static void main(String[] args) {
System.out.println("${helloWorld}");
}
}
执行FreemarkerDemo的main方法,会在com.example.service包下生成一个新的类:AutoCodeDemo.java
上面的方式可以在代码中使用了,但是不够友好,侵入性大。下面使用通用的工具类来实现相关功能:
编写一个freemarker的service作为工具类:
package com.example.service;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class FreemarkerService {
private Logger logger = LoggerFactory.getLogger(FreemarkerService.class);
private Map<String, Template> templateMap = new ConcurrentHashMap<>();
private freemarker.template.Configuration cfg;
@PostConstruct
public void init() throws IOException {
cfg = new freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_0);
cfg.setDirectoryForTemplateLoading(new File(FreemarkerService.class.getResource("/templates/ftls").getPath()));//这里加载模板文件存放的路径
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateUpdateDelayMilliseconds(0);//设置在检查是否存在比缓存模板更新版本的模板“文件”之前必须经过的时间(以毫秒为单位)。 默认为5000毫秒。
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
}
public String process(String tempName, Map conditions) {
try {
if (!templateMap.containsKey(tempName)) {
templateMap.put(tempName, cfg.getTemplate(tempName));
}
StringWriter out = new StringWriter();
templateMap.get(tempName).process(conditions, out);
String rs = out.getBuffer().toString();
out.close();
return rs;
} catch (TemplateException | IOException e) {
logger.error("生成文本失败:", e);
}
return null;
}
public String process(String tempName, String key, Object value) {
Map conditions = new HashMap();
conditions.put(key, value);
return process(tempName, conditions);
}
public void clearAll() {
templateMap.clear();
}
}
然后ftl模板文件不放在src/main/java目录下,而是放到src/main/resources目录下。在src\main\resources\templates\ftls目录下新建test1.ftl:
Hello ${name} from resource freemark! age=${age}
在controller中添加测试方法:
@Autowired
private FreemarkerService freemarkerService;
@RequestMapping("test1")
@ResponseBody
public String test1(){
HashMap<Object, Object> map = new HashMap<>();
map.put("name","张三");
map.put("age",20);
String s = freemarkerService.process("test1.ftl", map);
log.info(s);
return s;
}
下面会介绍freemarker的一些语法以及使用demo。读者可以根据输出理解freemarker的语法。
在controller中添加一个测试的入口
@RequestMapping("test2")
@ResponseBody
public String test2() {
Map<String, Object> map = new HashMap<>();
map.put("name", "FreeMarker是一款用java语言编写的模版引擎");
map.put("dateTime", new Date());
List<SysUser> users = new ArrayList<>();
users.add(new SysUser(1, "liubenlong007", "111"));
users.add(new SysUser(2, "欢迎", "13333333333"));
users.add(new SysUser(3, "You!", null));
map.put("users", users);
String s = freemarkerService.process("test2.ftl", map);
log.info(s);
return s;
}
test2.ftl:
字符串输出:
${"Hello ${name} !"} / ${"Hello " + name + " !"}
<#assign cname=r"特殊字符完成输出(http:\www.baidu.com)">
${cname}
字符串截取 :
通过下标直接获取下标对应的字母: ${name[2]}
起点下标..结尾下标截取字符串:${name[0..5]}
算数运算:
<#-- 支持"+"、"-"、"*"、"/"、"%"运算符 -->
<#assign number1 = 10>
<#assign number2 = 5>
"+" : ${number1 + number2}
"-" : ${number1 - number2}
"*" : ${number1 * number2}
"/" : ${number1 / number2}
"%" : ${number1 % number2}
比较运算符:
<#if number1 + number2 gte 12 || number1 - number2 lt 6>
"*" : ${number1 * number2}
<#else>
"/" : ${number1 / number2}
#if>
内建函数:
<#assign data = "abcd1234">
第一个字母大写:${data?cap_first}
所有字母小写:${data?lower_case}
所有字母大写:${data?upper_case}
<#assign floatData = 12.34>
数值取整数:${floatData?int}
获取集合的长度:${users?size}
时间格式化:${dateTime?string("yyyy-MM-dd")}
<#--
!:指定缺失变量的默认值 [下面user.phone指定了默认值]
??:判断某个变量是否存在,返回boolean值
-->
空判断和对象集合:
<#if users??>
<#list users as user >
${user.id} - ${user.name} - ${user.phone!"--"}
#list>
<#else>
${user!"变量为空则给一个默认值"}
#if>
Map集合:
<#assign mapData={"name":"程序员", "salary":15000}>
直接通过Key获取 Value值:${mapData["name"]}
通过Key遍历Map:
<#list mapData?keys as key>
Key: ${key} - Value: ${mapData[key]}
#list>
通过Value遍历Map:
<#list mapData?values as value>
Value: ${value}
#list>
List集合:
<#assign listData=["ITDragon", "blog", "is", "cool"]>
<#list listData as value>${value} #list>
include指令:
引入其他文件:<#include "test3.ftl" />
${otherName}<#--引入的内容是可以直接使用的-->
<@addMethod a=1 b=2 />
macro宏指令:
<#macro mo>
定义无参数的宏macro--${name}
#macro>
使用宏macro: <@mo />
<#macro moArgs a b c>
定义带参数的宏macro-- ${a+b+c}
#macro>
使用带参数的宏macro: <@moArgs a=1 b=2 c=3 />
命名空间:
<#import "test3.ftl" as otherFtl>
${otherFtl.otherName}
<@otherFtl.addMethod a=10 b=20 />
<#assign otherName="修改otherFreeMarker.ftl中的otherName变量值"/><#--这样不行,需要指定命名空间-->
${otherFtl.otherName}
<#assign otherName="修改otherFreeMarker.ftl中的otherName变量值" in otherFtl />
${otherFtl.otherName}
test3.ftl:
其他FreeMarker文件
<#macro addMethod a b >
result : ${a + b}
#macro>
<#assign otherName="另外一个FreeMarker的变量">
启动项目,执行方法输出如下:
字符串输出:
Hello FreeMarker是一款用java语言编写的模版引擎 ! / Hello FreeMarker是一款用java语言编写的模版引擎 !
特殊字符完成输出(http:\www.baidu.com)
字符串截取 :
通过下标直接获取下标对应的字母: e
起点下标..结尾下标截取字符串:FreeMa
算数运算:
"+" : 15
"-" : 5
"*" : 50
"/" : 2
"%" : 0
比较运算符:
"*" : 50
内建函数:
第一个字母大写:Abcd1234
所有字母小写:abcd1234
所有字母大写:ABCD1234
数值取整数:12
获取集合的长度:3
时间格式化:2019-03-19
空判断和对象集合:
1 - liubenlong007 - 111
2 - 欢迎 - 13333333333
3 - You! - --
Map集合:
直接通过Key获取 Value值:程序员
通过Key遍历Map:
Key: name - Value: 程序员
Key: salary - Value: 15,000
通过Value遍历Map:
Value: 程序员
Value: 15,000
List集合:
ITDragon blog is cool
include指令:
引入其他文件:其他FreeMarker文件
另外一个FreeMarker的变量
result : 3
macro宏指令:
使用宏macro: 定义无参数的宏macro--FreeMarker是一款用java语言编写的模版引擎
使用带参数的宏macro: 定义带参数的宏macro-- 6
命名空间:
另外一个FreeMarker的变量
result : 30
另外一个FreeMarker的变量
修改otherFreeMarker.ftl中的otherName变量值
更多详细的语法请参考: