SpringBoot整合Freemarker

一、模版引擎对比说明

在Java里面常用的模版引擎有很多, 例如: jspFreemarkerThymeleaf等 我们来对比一下各个模版引擎.

  • JSP(后端渲染,消耗性能) Java Server Pages 动态网页技术,由应用服务器中的JSP引擎来编译和执行,再将生成的整个页面返回给客户端,可以写java代码,持表达式语言(eljstl),内建函数,但是占用JVM内存。 是javaweb官方推荐, 但springboot不推荐
  • Freemarker FreeMarker Template Language(FTL)  文件一般保存为 xxx.ftl(目前新版本文件类型改为了ftlh) 严格依赖MVC模式,不依赖Servlet容器(不占用JVM内存),内建函数
  • Thymeleaf (主推) 轻量级的模板引擎(负责逻辑业务的不推荐,解析DOM或者XML会占用多的内存) 可以直接在浏览器中打开且正确显示模板页面,直接是html结尾,直接编辑

二、基于freemarker进行springboot项目的搭建

2.1.创建一个springboot项目或者模块,这里创建一个模块

输入项目名称和包名,选择项目类型和jdk版本

2.2.在pom.xml中引入依赖

这里引入了后续所需要的所有依赖

<!--mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!--MySQL数据库连接的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>

        <!-- pagehelper分页依赖 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.12</version>
        </dependency>

        <!--添加druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!--引入freemarker依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
            <version>2.7.5</version>
        </dependency>

2.3.在项目中添加配置文件application.yml,内容如下

spring:
  datasource:
    # 使用阿里的Druid连接池
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 填写你数据库的url、登录名、密码和数据库名
    url: jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: test
    password: 123456
  druid:
    # 连接池的配置信息
    # 初始化大小,最小,最大
    initial-size: 5
    min-idle: 5
    maxActive: 20
    # 配置获取连接等待超时的时间
    maxWait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall,slf4j
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
    # 配置DruidStatFilter
    web-stat-filter:
      enabled: true
      url-pattern: "/*"
      exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
    # 配置DruidStatViewServlet
    stat-view-servlet:
      url-pattern: "/druid/*"
      # IP白名单(没有配置或者为空,则允许所有访问)
      allow: 127.0.0.1,192.168.8.109
      # IP黑名单 (存在共同时,deny优先于allow)
      deny: 192.168.1.188
      #  禁用HTML页面上的“Reset All”功能
      reset-enable: false
      # 登录名
      login-username: admin
      # 登录密码
      login-password: 123456
#  freemarker前缀和后缀会自动添加:
#    suffix:
#    prefix:

mybatis:
  mapper-locations: classpath:mybatis/*.xml
  type-aliases-package: com.augus.pojo

2.4.在resource下添加lognback方便调试

内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="${catalina.base}/logs/" />
    <!-- 控制台输出 -->
    <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
        <!-- 日志输出格式 -->
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="RollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
            </pattern>
        </layout>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>
    <!-- 日志输出级别 -->
    <root level="debug">
        <appender-ref ref="Stdout" />
        <appender-ref ref="RollingFile" />
    </root>
    <logger name="com.augus.mapper" level="DEBUG"></logger>
    <!--日志异步到数据库 -->
    <!--<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
            日志异步到数据库
            <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
               连接池
               <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
                  <driverClass>com.mysql.jdbc.Driver</driverClass>
                  <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
                  <user>root</user>
                  <password>root</password>
                </dataSource>
            </connectionSource>
      </appender> -->
</configuration>

2.5.在com.augus下创建包controller

在controller包中创建 ShowController,内容如下:

package com.augus.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
@RequestMapping("/freeMarker")
public class ShowController {
    @RequestMapping("/show")
    public String freeMarkerShow(Map<String, Object> map){
        //向前端传递数据
        map.put("name", "梦特娇");
        //返回模块文件名称
        return "show";
    }

2.6.在resource下在创建项目的时候,会自动创建一个templates包,里面添加模板文件

在下面创建 show.ftlh 文件,内容如下:

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

<#--引入文件内容-->
${name}
</body>
</html>

2.7.运行项目进行测试

执行主方法启动项目后,在浏览器运行访问:http://localhost:8080/freeMarker/show 如下图,则表示环境搭建完成

三、FreeMarker遍历list集合

实现需求,从emp表中读取数据

3.1.在resource下创建包mybatis存放mapper映射文件

在下面创建EmpMapper.xml内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.augus.mapper.EmpMapper">
    <!--查询所有的员工信息-->
    <select id="queryAllEmp" resultType="emp">
        select * from emp
    </select>
</mapper>

3.2.在com.augus包下创建创建pojo,存方实体类

在下面创建Emp表的实体类,内容如下:

package com.augus.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Date;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Emp implements Serializable {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;
}

3.3.创建mapper包

在里面创建EmpMapper接口

package com.augus.mapper;

import com.augus.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface EmpMapper {
    List<Emp> queryAllEmp();
}

3.4.创建service包

在下面创建接口 EmpService,内容如下

package com.augus.service;

import com.augus.pojo.Emp;
import com.github.pagehelper.PageInfo;

import java.util.List;

public interface EmpService {
    List<Emp> findAll();
}

在service下创建impl包,存放实体类,在里面新建EmpServiceImpl类,内容如下

package com.augus.service.impl;

import com.augus.mapper.EmpMapper;
import com.augus.pojo.Emp;
import com.augus.service.EmpService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmpServiceImpl implements EmpService {
    @Autowired
    private EmpMapper empMapper;

    @Override
    public List<Emp> findAll() {
        return empMapper.queryAllEmp();
    }
}

3.5.创建包controller

在下面创建类EmpController,将查询出来的员工信息使用list集合存放,传递到前端页面

package com.augus.controller;

import com.augus.pojo.Emp;
import com.augus.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;

@Controller
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;

    @RequestMapping("/findAllList")
    @ResponseBody
    public ModelAndView findAll(){
        //创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //查询出来所有员工信息
        List<Emp> all = empService.findAll();
        //将员工信息添加到到视图,传递到前端页面
        modelAndView.addObject("empList", all);
        //设置视图跳转地址
        modelAndView.setViewName("showEmp");
        return modelAndView;
    }
}

3.6.在templates下创建 showEmp.ftlh ,遍历集合

说明: _index:得到循环的下标,使用方法是在stu后边加"_index",它的值是从0开始 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #empTable{
            width: 80%;
            border: 1px solid blue;
            margin: 0px auto;
        }
        #empTable th,td{
            border: 1px solid green;
            text-align: center;
        }
    </style>
</head>
<body>
<table id="empTable" cellpadding="0px" cellspacing="0px">
    <tr>
        <th>索引</th>
        <th>工号</th>
        <th>姓名</th>
        <th>岗位</th>
        <th>薪资</th>
        <th>部门号</th>
    </tr>
    <#--获取后端查询后传递过来的数据
    empList是一个列表,这里是属于遍历列表输出的 emp就是一个变量名
    -->
    <#list empList as emp>
        <tr>
            <#--
            emp_index:这个是得到循环的下标,使用方法是在变量后面加_index,值从0开始
            -->
            <td>${emp_index}</td>
            <td>${emp.empno}</td>
            <td>${emp.ename}</td>
            <td>${emp.job}</td>
            <td>${emp.sal}</td>
            <td>${emp.deptno}</td>
        </tr>
    </#list>
</table>
</body>
</html>

3.7.启动项目,进行测试

在浏览器访问:http://localhost:8080/emp/findAllList,如图所示

四、FreeMarker遍历集合

4.1.在EmpController中添加如下控制器

FreeMarker在遍历map集合是,key必须是String

package com.augus.controller;

import com.augus.pojo.Emp;
import com.augus.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;

@Controller
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;

    @RequestMapping("/findAllList")
    @ResponseBody
    public ModelAndView findAll(){
        //创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //查询出来所有员工信息
        List<Emp> all = empService.findAll();
        //将员工信息添加到到视图,传递到前端页面
        modelAndView.addObject("empList", all);
        //设置视图跳转地址
        modelAndView.setViewName("showEmp");
        return modelAndView;
    }

    @RequestMapping("/findAllMap")
    @ResponseBody
    public ModelAndView findAllMap(){
        //创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //查询出来所有员工信息
        List<Emp> empAll = empService.findAll();

        //将获取回来使用list集合存放的数据采用转换成map存放
        HashMap<String, Emp> empMap = new HashMap<>();

        //遍历list集合,使用工号作为键,值为某一个员工
        for (Emp emp : empAll) {
            //将值添加到map集合
            //这里获取回来的值作为键必须是个字符串才行
            empMap.put(emp.getEmpno().toString(),emp);
        }


        //将存放员工信息的map集合添加到到视图,传递到前端页面
        modelAndView.addObject("empMap", empMap);
        //设置视图跳转地址
        modelAndView.setViewName("showEmpMap");
        return modelAndView;
    }
}

4.2.在resources的templates中添加模板文件

创建模板文件名为:showEmpMap.ftlh ,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #empTable{
            width: 80%;
            border: 1px solid blue;
            margin: 0px auto;
        }
        #empTable th,td{
            border: 1px solid green;
            text-align: center;
        }
    </style>
</head>
<body>
<table id="empTable" cellpadding="0px" cellspacing="0px">
    <tr>
        <th>索引</th>
        <th>工号</th>
        <th>姓名</th>
        <th>岗位</th>
        <th>薪资</th>
        <th>部门号</th>
    </tr>
    <#--
    这里遍历map集合,实际上遍历获取就是集合的键。那么这里在根据键取对应的值
    -->
    <#list empMap?keys as k>
        <tr>
            <#--
            emp_index:这个是得到循环的下标,使用方法是在变量后面加_index,值从0开始
            -->
            <td>${k_index}</td>
            <#--
            这里取的是empno值,但是咱们是以empno作为字典的键的,所以这里直接写键也行
            freeMaker在遍历map的时候,key必须是string
            -->
            <td>${empMap[k].empno}</td>
            <td>${empMap[k].ename}</td>
            <td>${empMap[k].job}</td>
            <td>${empMap[k].sal}</td>
            <td>${empMap[k].deptno}</td>
        </tr>
    </#list>
</table>
</body>
</html>

4.3.测试

重启项目,浏览器访问:http://localhost:8080/emp/findAllMap,如下所示则表示成功:

五、if指令和内置函数

5.1. if 指令

if 指令即判断指令,是常用的FTL指令,freemarker在解析时遇到if会进行判断,条件为真则输出if中间的内容,否 则跳过内容不再输出。

if中支持的运算符具体如下:

  1. 算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %
  2. 逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误
  3. 比较运算符有如下几个:
    • =或者== :判断两个值是否相等.
    • !=  判断两个值是否不等.
    • > 或者gt  判断左边值是否大于右边值
    • >=或者gte  判断左边值是否大于等于右边值
    • <或者lt  判断左边值是否小于右边值
    • <=或者lte  判断左边值是否小于等于右边值

注意:

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

如何判断空值

  • 判断某变量是否存在使用 “??” 用法为:variable??,如果该变量存在,返回true,否则返回false 例:为防止stus为空报错可以加上判断如下
  • 缺失变量默认值使用 “!” 使用!要以指定一个默认值,当变量为空时显示默认值。例: ${name!''}表示如果name为空显示空字符串。如果是嵌套对象则建议使用()括起来。

5.2.内置函数

内建函数语法格式: 变量+?+函数名称

5.2.1.内建函数获取某个集合的大小

${集合名?size}

5.2.2.内建函数日期格式化

  • 显示年月日: ${today?date}
  • 显示时分秒: ${today?time}
  • 显示日期+时间:${today?datetime}
  • 自定义格式化: ${today?string("yyyy年MM月")}

5.2.3.内建函数将json字符串转成对象

<#assign text="{'bank':'xx银行','account':'657801921201920212'}" />
<#assign data=text?eval />
<#--从data中取值-->
开户行:${data.bank} 账号:${data.account}

其中用到了 assign标签,assign的作用是定义一个变量。

5.3.案例演示

5.3.1.在EmpController中添加如下控制器

FreeMarker在遍历map集合是,key必须是String

package com.augus.controller;

import com.augus.pojo.Emp;
import com.augus.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;

@Controller
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;

    @RequestMapping("/findEmp")
    @ResponseBody
    public ModelAndView findEmp(){
        //创建ModelAndView对象
        ModelAndView modelAndView = new ModelAndView();
        //查询出来所有员工信息
        List<Emp> empAll = empService.findAll();

        //将存放员工信息的map集合添加到到视图,传递到前端页面
        modelAndView.addObject("empList", empAll);
        //设置视图跳转地址
        modelAndView.setViewName("showAllEmp");
        return modelAndView;
    }
}

5.3.2.在resources的templates中添加模板文件

创建模板文件名为:showAllEmp.ftlh ,内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #empTable{
            width: 80%;
            border: 1px solid blue;
            margin: 0px auto;
        }
        #empTable th,td{
            border: 1px solid green;
            text-align: center;
        }
    </style>
</head>
<body>
<table id="empTable" cellpadding="0px" cellspacing="0px">
    <tr>
        <th>索引</th>
        <th>工号</th>
        <th>姓名</th>
        <th>岗位</th>
        <th>上级主管</th>
        <th>入职日期1</th>
        <th>入职日期2</th>
        <th>入职日期3</th>
        <th>入职日期4</th>
        <th>薪资</th>
        <th>补助</th>
        <th>部门号</th>
    </tr>
    <#--
    这里遍历map集合,实际上遍历获取就是集合的键。那么这里在根据键取对应的值
    -->
    <#--这里是判断empList是否为空的-->
    <#if empList??>
        <#list empList as emp>
            <tr>
                <#--
                emp_index:这个是得到循环的下标,使用方法是在变量后面加_index,值从0开始
                -->
                <td>${emp_index}</td>
                <#--
                这里取的是empno值,但是咱们是以empno作为字典的键的,所以这里直接写键也行
                freeMaker在遍历map的时候,key必须是string
                -->
                <td>${emp.empno}</td>
                <#--
                判断,如果ename的值为张松溪,则给设置颜色
                -->
                <td <#if emp.ename == "张松溪">style="color: blueviolet" </#if>>
                    ${emp.ename}
                </td>
                <td>${emp.job}</td>
                <#--判断mgr字段的是否为空,如果为空,则直接显示无-->
                <td>${emp.mgr!'无'}</td>
                <#--
                时间日期的不同形式的显示
                -->
                <td>${emp.hiredate?date}</td>
                <td>${emp.hiredate?time}</td>
                <td>${emp.hiredate?datetime}</td>
                <td>${emp.hiredate?string("yyyy年MM月dd日")}</td>
                <#--判断薪资是否大于3000,大于3000变成红色-->
                <td <#if emp.sal gte 3000 > style="color: red" </#if>>
                    ${emp.sal}
                </td>
                <#--
                判断如果comm字段如果为空则显示为0
                -->
                <td>${emp.comm!'0'}</td>
                <td>${emp.deptno}</td>
            </tr>
        </#list>
    </#if>
</table>
</body>
</html>

5.3.2.测试

重启项目,在浏览器访问:http://localhost:8080/emp/findEmp,如下图,表示成功,

 注意:那个入职日期2,之所以显示为空,是因为我的数据中时分秒就是为空

posted @ 2022-11-11 17:36  酒剑仙*  阅读(1745)  评论(0编辑  收藏  举报