SpringBoot整合Freemarker
一、模版引擎对比说明
在Java里面常用的模版引擎有很多, 例如: jsp
、Freemarker
、Thymeleaf
等 我们来对比一下各个模版引擎.
- JSP(后端渲染,消耗性能)
Java Server Pages
动态网页技术,由应用服务器中的JSP
引擎来编译和执行,再将生成的整个页面返回给客户端,可以写java
代码,持表达式语言(el
、jstl
),内建函数,但是占用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中支持的运算符具体如下:
- 算数运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , %
- 逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误
- 比较运算符有如下几个:
- =或者== :判断两个值是否相等.
- != 判断两个值是否不等.
- > 或者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,之所以显示为空,是因为我的数据中时分秒就是为空