Java 模板引擎 ~ FreeMarker。
文章目录
数据模型 + 模板 = 输出(HTML)。
https://freemarker.apache.org/
FreeMarker 是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML 网页,电子邮件,配置文件,源代码等)的通用工具。ta 不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。
异步。
package com.geek.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class SpringbootdemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootdemoApplication.class, args);
}
}
package com.geek.springbootdemo.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
// 耗时操作,在一个子线程中执行。
@Async
public void show() {
System.out.println(2);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(3);
}
}
package com.geek.springbootdemo.controller;
import com.geek.springbootdemo.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@RequestMapping("/async")
public String testAsync() {
System.out.println(1);
asyncService.show();// 由主线程执行,开启了一个子线程。
System.out.println(4);
return "success";
}
}
1
4
2
3
多环境开发。
application-dev.properties
application-prod.properties
application-test.properties
application.properties(Spring Boot 默认加载)。
spring.profiles.active=test
Spring Boot 整合 jsp。
-
必须是 web 工程。
-
不能使用 Spring Boot 的 tomcat。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
报 404 问题,不知为啥。
但使用 ↓ ↓ ↓ 可以解决。
spring-boot:run
Maven 构建 FreeMarker。
jsp 动态页面,加载慢。
FreeMarker 不需要 tomcat 等 Servlet。最终商将页面转为 html。
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
- first.ftl。
根据这个模板生成 html 文件。
${name}
package com.geek.freemarker;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Demo00 {
public static void main(String[] args) throws IOException, TemplateException {
// 创建模板文件 first.ftl。
// 创建配置对象。
Configuration configuration = new Configuration();
// 配置模板文件的路径。
configuration.setDirectoryForTemplateLoading(new File("G:\\lyfGeek\\IdeaProjects\\freemarker_geek\\freeemarkerdemo\\src\\main\\webapp\\WEB-INF\\ftl"));
// 模板使用的编码。
configuration.setDefaultEncoding("utf-8");
// 得到模板文件 Template 对象。
Template template = configuration.getTemplate("first.ftl");
// 构建模板使用的数据。
Map map = new HashMap();
map.put("name", "zhangsan");
// 设置最终生成的静态页面的路径和名称。
FileWriter fileWriter = new FileWriter("./first.html");
// 生成。
template.process(map, fileWriter);
fileWriter.close();
}
}
FreeMarker 取值实体类。
package com.geek.freemarker;
public class Student {
private int id;
private String name;
private int age;
private String address;
public Student() {
}
public Student(int id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 生成。
package com.geek.freemarker;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Demo01 {
public static void main(String[] args) throws IOException, TemplateException {
Configuration configuration = new Configuration();
configuration.setDirectoryForTemplateLoading(new File("G:\\lyfGeek\\IdeaProjects\\freemarker_geek\\freeemarkerdemo\\src\\main\\webapp\\WEB-INF\\ftl"));
configuration.setDefaultEncoding("UTF-8");
Template template = configuration.getTemplate("second.ftl");
// 构建数据模型。
Student student = new Student();
student.setId(11);
student.setName("李四");
student.setAge(25);
student.setAddress("武汉");
Map map = new HashMap();
map.put("stu", student);
FileWriter fileWriter = new FileWriter("./second.html");
template.process(map, fileWriter);
fileWriter.close();
}
}
- 模板文件。
<html>
<head>
</head>
<body>
学号:${stu.id}
姓名:${stu.name}
年龄:${stu.age}
住址:${stu.address}
</body>
</html>
- 生成的 html 文件。
<html>
<head>
</head>
<body>
学号:11
姓名:李四
年龄:25
住址:武汉
</body>
</html>
FreeMarker 取值 List。
<html>
<head></head>
<body>
<table border="1" cellspacing="0" cellpadding="0">
<tr>
<td>编号</td>
<td>学号</td>
<td>姓名</td>
<td>年龄</td>
<td>住址</td>
</tr>
<#list stulist as stu>
<tr>
<td>${stu_index}</td>
<td>${stu.id}</td>
<td>${stu.name}</td>
<td>${stu.name}</td>
<td>${stu.address}</td>
</tr>
</#list>
</table>
</body>
</html>
package com.geek.freemarker;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Demo02 {
public static void main(String[] args) throws IOException, TemplateException {
Configuration configuration = new Configuration();
configuration.setDirectoryForTemplateLoading(new File("G:\\lyfGeek\\IdeaProjects\\freemarker_geek\\freeemarkerdemo\\src\\main\\webapp\\WEB-INF\\ftl"));
configuration.setDefaultEncoding("utf-8");
Template template = configuration.getTemplate("third.ftl");
List<Student> list = new ArrayList<>();
Student student = new Student(11, "王五", 25, "武汉");
Student student2 = new Student(22, "王五2", 25, "上海");
Student student3 = new Student(33, "王五3", 25, "北京");
list.add(student);
list.add(student2);
list.add(student3);
Map map = new HashMap();
map.put("stulist", list);
FileWriter fileWriter = new FileWriter("./third.html");
template.process(map, fileWriter);
fileWriter.close();
}
}
<html>
<head></head>
<body>
<table border="1" cellspacing="0" cellpadding="0">
<tr>
<td>编号</td>
<td>学号</td>
<td>姓名</td>
<td>年龄</td>
<td>住址</td>
</tr>
<tr>
<td>0</td>
<td>11</td>
<td>王五</td>
<td>王五</td>
<td>武汉</td>
</tr>
<tr>
<td>1</td>
<td>22</td>
<td>王五2</td>
<td>王五2</td>
<td>上海</td>
</tr>
<tr>
<td>2</td>
<td>33</td>
<td>王五3</td>
<td>王五3</td>
<td>北京</td>
</tr>
</table>
</body>
</html>
日期。
map.put("date", new Date());
- ftl。
${date}
会报错。
提示解决方式。
----
Tip: Use ?date, ?time, or ?datetime to tell FreeMarker the exact type.
----
Tip: If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), to specify which fields to display.
----
六月 25, 2020 11:42:15 下午 freemarker.log._JULLoggerFactory$JULLogger error
严重: Error executing FreeMarker template
FreeMarker template error:
Can't convert the date-like value to string because it isn't known if it's a date (no time part), time or date-time value.
The blamed expression:
==> date [in template "third.ftl" at line 6, column 3]
----
Tip: Use ?date, ?time, or ?datetime to tell FreeMarker the exact type.
----
Tip: If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), to specify which fields to display.
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${date} [in template "third.ftl" at line 6, column 1]
----
Java stack trace (for programmers):
----
freemarker.core._TemplateModelException: [... Exception message was already printed; see it above ...]
at freemarker.core._MessageUtil.newCantFormatUnknownTypeDateException(_MessageUtil.java:301)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1660)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1645)
at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:380)
at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:358)
at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:100)
at freemarker.core.DollarVariable.accept(DollarVariable.java:63)
at freemarker.core.Environment.visit(Environment.java:330)
at freemarker.core.Environment.visit(Environment.java:336)
at freemarker.core.Environment.process(Environment.java:309)
at freemarker.template.Template.process(Template.java:384)
at com.geek.freemarker.Demo02.main(Demo02.java:30)
Caused by: freemarker.core.UnknownDateTypeFormattingUnsupportedException: Can't convert the date-like value to string because it isn't known if it's a date (no time part), time or date-time value.
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1722)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1492)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1658)
... 10 more
Exception in thread "main" FreeMarker template error:
Can't convert the date-like value to string because it isn't known if it's a date (no time part), time or date-time value.
The blamed expression:
==> date [in template "third.ftl" at line 6, column 3]
----
Tip: Use ?date, ?time, or ?datetime to tell FreeMarker the exact type.
----
Tip: If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), to specify which fields to display.
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: ${date} [in template "third.ftl" at line 6, column 1]
----
Java stack trace (for programmers):
----
freemarker.core._TemplateModelException: [... Exception message was already printed; see it above ...]
at freemarker.core._MessageUtil.newCantFormatUnknownTypeDateException(_MessageUtil.java:301)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1660)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1645)
at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:380)
at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:358)
at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:100)
at freemarker.core.DollarVariable.accept(DollarVariable.java:63)
at freemarker.core.Environment.visit(Environment.java:330)
at freemarker.core.Environment.visit(Environment.java:336)
at freemarker.core.Environment.process(Environment.java:309)
at freemarker.template.Template.process(Template.java:384)
at com.geek.freemarker.Demo02.main(Demo02.java:30)
Caused by: freemarker.core.UnknownDateTypeFormattingUnsupportedException: Can't convert the date-like value to string because it isn't known if it's a date (no time part), time or date-time value.
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1722)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1492)
at freemarker.core.Environment.getTemplateDateFormat(Environment.java:1658)
... 10 more
Process finished with exit code 1
Spring Boot 整合 FreeMarker。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.geek</groupId>
<artifactId>springboot-freemarker</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-freemarker</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<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.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- ftl 模板文件放在 templates/page.ftl。
${address}
- controller 层。
package com.geek.springbootfreemarker.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
@Controller
public class TestController {
@RequestMapping("/toPage")
public String show(Map<String, Object> map) {
map.put("address", "武汉");
return "page";
}
}
- 访问 http://localhost:8080/toPage。页面会显示
武汉