spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成

概述

FreeMarker是一款用java语言编写的模版引擎,它虽然不是web应用框架,但它很合适作为web应用框架的一个组件。

特点:

  1. 轻量级模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中
  2. 能生成各种文本,如html,xml,java,等
  3. 入门简单,它是用java编写的,很多语法和java相似

工作原理:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成

引入相关依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

<!--bootstrap相关-->
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>3.3.5</version>
    </dependency>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.1.1</version>
    </dependency>
    
        <!--freemarker-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>${testng.version}</version>
        <scope>test</scope>
    </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文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
this is index page
</body>
</html>

编写welcome.ftl文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</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;
    }
}

启动服务,测试:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成
接下来先写一个复杂一点的

@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:

<!DOCTYPE html>
<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 Name</th>
                    <th>Last Name</th>
                    <th>User Name</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td>aehyok</td>
                    <td>leo</td>
                    <td>@aehyok</td>
                </tr>
                <tr>
                    <td>lynn</td>
                    <td>thl</td>
                    <td>@lynn</td>
                </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>

启动服务,访问:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成
至此,项目目录结构如下:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成

代码中使用freemarker

上面都是讲的返回页面使用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工具类

上面的方式可以在代码中使用了,但是不够友好,侵入性大。下面使用通用的工具类来实现相关功能:
编写一个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;
}

启动服务,访问:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成
至此,完整的目录结构:
spring boot 2.1学习笔记【二十】SpringBoot 2 freemarker bootstrap 集成