Freemarker 语法精粹
文章目录
- 说明
- 基本用法
- 宏
- 加载宏定义
- 宏文件写法
- import和include区别
- 内置方法
- 注册全局共享变量
- 处理空值和默认值
- 获得hashmap的键值
- 从map中拿对象
- 遍历Map
- 其它小技巧
- 迁移事项
- 参考
说明
Freemarker 还存在我的一些老项目中,比起前端框架,自有它的简便之处,前后端合体的项目还是有用武之地的。Freemarker渲染性能也不差,为何一定要放弃呢?
基本用法
Java:
dataMap.put(“user”,new User());
ftl:
${user.getName()}
……
<#if (user.name)?? && user.name == ‘admin’>
……
</#if>
宏
没用过宏,就不算用过freemarker。宏是组件复用的方式。
注意:在IDEA中要创建freemarker_implicit.ftl文件,在里面配置模板路径,这样include一个模板文件时IDEA才能识别。
加载宏定义
定义一个宏的文件导入文件macro.ftl:
<#import "pagination.ftl" as pagination>
<#import "article-detail-listview.ftl" as ArticleListview>
然后,在FreemarkerConfig.java中加载:
@Beanpublic FreeMarkerConfigurer freeMarkerConfigurer() {//写入配置FreeMarkerConfigurer factory = new FreeMarkerConfigurer();writerProperties(factory);//创建fm的配置,并且将factory中的信息写入到configuration中freemarker.template.Configuration configuration = null;try {configuration = factory.createConfiguration();//和spring boot不同的部分,这部分是用来写入我们需要的freemarker configurationList<String> autoIncludes = Lists.newArrayListWithCapacity(1);//注意macro\macro.ftl这个路径是和在spring.freemarker.template-loader-path下autoIncludes.add("macro/macro.ftl");configuration.setAutoIncludes(autoIncludes);configuration.setClassForTemplateLoading(this.getClass(), "/templates/");} catch (IOException e) {e.printStackTrace();} catch (TemplateException e) {e.printStackTrace();}factory.setConfiguration(configuration);return factory;}
宏文件写法
<#macro pagination target route totalPages pageNo startPageNo endPageNo><script>var currentPageNo = ${pageNo};var startPageNo = ${startPageNo};var endPageNo = ${endPageNo};function movePagination(target, direction){if(direction == 1){startPageNo = endPageNo + 1;}else if (direction == -1){startPageNo = startPageNo - 10;}if(startPageNo <= 0){startPageNo = 1;}endPageNo = startPageNo + 9;currentPageNo = startPageNo;goPage(target, currentPageNo, startPageNo, endPageNo);}function goPage(target, pageNo, startPageNo, endPageNo){currentPageNo = pageNo;startPageNo = startPageNo;endPageNo = endPageNo;var route = "${route}";if(route.indexOf("?") == -1){route = route + "?1=1";}var url = "${base}/" + route + "&pageNo=" + pageNo + "&startPageNo=" + startPageNo + "&endPageNo=" + endPageNo;console.log(url)if (target.length > 0) {$("#"+target).load(url);} else {window.location.href = url;}}</script><#assign recordsPerPage=10/><div class="pagination-x"><div style="text-align: center"><ul><#if (pageNo > 1)><#assign start = (pageNo - 2) * recordsPerPage/><a href="javascript:movePagination('${target}', -1);">上一页</a></#if><#if (totalPages > 10)><#-- ------------------------------ --><#-- Always write the first 3 links --><#-- ------------------------------ --><#list 1 .. 3 as page><#if (page==pageNo)><li class="active"><span class="current">${page}</span></li><#else><li><a href="javascript:goPage('${target}', ${page}, ${startPageNo}, ${endPageNo})" class="page-link">${page}</a></li></#if></#list><#-- ------------------ --><#-- Intermediate links --><#-- ------------------ --><#if (pageNo > 1 && pageNo < totalPages)><#if (pageNo > 5)><span class="gensmall">...</span></#if><#if (pageNo > 4)><#assign min = pageNo - 1/><#else><#assign min = 4/></#if><#if (pageNo < totalPages - 4)><#assign max = pageNo + 2/><#else><#assign max = totalPages - 2/></#if><#if (max >= min + 1)><#list min .. max - 1 as page><#if (page==pageNo)><li class="active"><span class="current">${page}</span></li><#else><li><a href="javascript:goPage('${target}', ${page}, ${startPageNo}, ${endPageNo})" class="page-link">${page}</a></li></#if></#list></#if><#if (pageNo < totalPages - 4)><span class="gensmall">...</span></#if><#else><span class="gensmall">...</span></#if><#-- ---------------------- --><#-- Write the last 3 links --><#-- ---------------------- --><#list totalPages - 2 .. totalPages as page><#if (page==pageNo)><li class="active"><span class="current">${page}</span></li><#else><li><a href="javascript:goPage('${target}', ${page}, ${startPageNo}, ${endPageNo})" class="page-link">${page}</a></li></#if></#list><#else><#list 1 .. totalPages as page><#if (page==pageNo)><li class="active"><span class="current">${page}</span></li><#else><li><a href="javascript:goPage('${target}', ${page}, ${startPageNo}, ${endPageNo})" class="page-link">${page}</a></li></#if></#list></#if><#-- ------------- --><#-- Next page --><#-- ------------- --><#if (pageNo < totalPages)><#assign start = pageNo * recordsPerPage/><a href="javascript:movePagination('${target}', 1);">下一页</a></#if></ul></div></div>
</#macro>
import和include区别
在使用freemarker作为前端页面模板的应用中,会有很多的freemarker模板页面,这些ftl会在不同的页面中重复使用,一是为了简化布局的管理,二是可以重复使用一些代码。
在freemarker中可以通过以下两种方式来使用已经存在的模板。他们是<#inclue>和<#import>标签。
1.<#include> directive
该标签的作用是将便签中指定的路径的ftl文件导入到使用标签的ftl文件中,包括macro\funtion\variable等所有被引用的ftl内容。被引用的ftl内容会在引用的ftl中重新被渲染最终输出。一般用于页面拆分,便于页面重用,如将header和footer分别抽取出来独自成模板,这样在所有返回给前端的page里都可以include这两个模板了。
<#include “…/…/header.ftl”> 将相对路径中的header.ftl文件加载到当前文件中。如header.ftl中定义了宏、函数等,在当前文件中可以不加命名空间前缀直接使用。如在header.ftl中
定义了<#marco getBranch>,可以在当前文件中直接使用:<@getBranch>…/@getBranch.
2.<#import> directive
该标签的字面意义和include差不多,经常会混淆使用。其含义是将标签中指定的模板中的已定义的宏、函数等导入到当前模板中,并在当前文档中指定一个变量作为该模板命名空间,以便当前文档引用。与include的区别是该指令不会讲import指定的模板内容渲染到引用的模板的输出中。
如:<#import ”…/…/service.ftl as service>.其作用是将service.ftl中的定义的各宏、函数、变量、自定义、设置等内容用指定的命名空间名称加以引用。但是当前文档不会将import的模板输出插入到import标签的位置。和<#include>标签一样可以使用相对路径和绝对路径引用外部模板。
如:service.ftl中定义的宏如下:<#macro branchService></#macro>,在当前文档中可以这样导入<#import “…/…/service.ftl” as service> ,service变量作为该文档中使用service中服务的命名空间,调用时应该这样:<@service.branchService >…/@service.branchService.
总结:import比include好,能防止include多次。
内置方法
Freemarker有很多内置方法,操作常见数据类型比较方便。
采用?调用,例如:
“abcabc”?index_of(“bc”)
“123”?number // 转为数字
注册全局共享变量
可以把页面中公共变量,函数注册进去,这样每个模版都能轻松使用:
@PostConstructpublic void setConfigure() throws Exception {configuration.setSharedVariable("base", "/cms");configuration.setSharedVariable("title", "CMS知识库");configuration.setSharedVariable("siteTitle", "CMS知识库");configuration.setSharedVariable(CMSConstants.CMS_CONFIG, new CMSConfig());configuration.setSharedVariable(CMSConstants.CURRENT_CHANNEL, "");// 配置常用函数configuration.setSharedVariable("randomNumber", new RandomNumberFunction());configuration.setSharedVariable("shuffle", new ShuffleFunction());
// configuration.setSharedVariable(CMSConstants.CURRENT_USER, new User());}
处理空值和默认值
Welcome ${user!}!
Welcome ${user!‘your name’}!
或者
${user?if_exists}
${user?default(‘your name’)}
防止null:
${category.categoryName!}
${user.name!‘想要展示的内容’}
获得hashmap的键值
categoryCache.get(bs.categoryId?c) // int to string
categoryCache[bs.categoryId?c] // int to string
从map中拿对象
<#assign category = categoryCache[bs.categoryId?c].get()>
map中取值, c转为String
<#assign category = categoryCache[bs.categoryId?c].get()>
遍历Map
<#list categoryCache?keys as key><#assign category = categoryCache[key].get()>${category.categoryName}</#list>
<#if name??>
其它小技巧
判断List是否为空
<#if orgList?? && (orgList?size > 0) >
<#else>
日期格式化:
${ebook.requestDate?string(“yyyy-MM-dd hh:mm:ss”)}
迁移事项
FileTemplateLoader和ClassTemplateLoader,原来是从classpath中加载的,include时路径按照classpath中的路径,但现在是从文件系统中加载,路径就得采用相对路径,即每个template去include别人时,路径要相当于该模板的路径。
参考
- https://freemarker.apache.org/docs/index.html
相关文章:
Freemarker 语法精粹
文章目录说明基本用法宏加载宏定义宏文件写法import和include区别内置方法注册全局共享变量处理空值和默认值获得hashmap的键值从map中拿对象遍历Map其它小技巧迁移事项参考说明 Freemarker 还存在我的一些老项目中,比起前端框架,自有它的简便之处&…...
使用Benchto框架对Trino进行SQL性能对比测试
有时需要对魔改源码前后的不同版本Trino引擎进行性能对比测试,提前发现改造前后是否有性能变差或变好的现象,避免影响数据业务的日常查询任务性能。而Trino社区正好提供了一个性能测试对比框架:GitHub - trinodb/benchto: Framework for runn…...
Redis之哨兵模式
什么是哨兵模式? Sentinel(哨兵)是用于监控Redis集群中Master状态的工具,是Redis高可用解决方案,哨兵可以监视一个或者多个redis master服务,以及这些master服务的所有从服务。 某个master服务宕机后,会把这个master下…...
Selenium自动化测试Python二:WebDriver基础
欢迎阅读WebDriver基础讲义。本篇讲义将会重点介绍Selenium WebDriver的环境搭建和基本使用方法。 WebDriver环境搭建 Selenium WebDriver 又称为 Selenium2。 Selenium 1 WebDriver Selenium 2 WebDriver是主流Web应用自动化测试框架,具有清晰面向对象 API&…...
蓝桥杯模块学习17——AT24C02存储器(深夜学习——单片机)
一、硬件电路:1、引脚功能:(1)A0-A2:决定不同设备的地址码:(2)WP:写保护二、通讯方式(IIC协议)通讯方式与PCF8591相同,可参考以下文章…...
netty
Netty的介绍Netty是异步的(指定回调处理)、基于事件驱动的网络应用框架,用于快速开发高性能、高可靠性的网络IO程序。Netty本质是一个NIO框架,适用于服务器通讯相关的多种应用场景,分布式节点远程调用中Netty往往作为R…...
Django项目部署-uWSGI
Django项目部署-uWSGIDjango运维部署框架整体部署架构web服务器与web应用服务器的区别部署环境准备安装python3安装mariadb安装Django和相关模块Django托管服务器uWSGI使用uWSGI配置使用Django运维部署框架 整体部署架构 操作系统: Linux 。优势:生态系统丰富&…...
jhipster自动生成java代码的方法
一、前言 java springboot后台项目用到了jpa查询数据库,还用到了jhipster,这个东西可以自动生成基础的Controller、Service、Dao、JavaBean等相关代码,减少重复开发。 在此总结下使用方法。 二、jhipster自动生成java代码的方法 1.需要先…...
LeetCode 82. 删除排序链表中的重复元素 II
原题链接 难度:middle\color{orange}{middle}middle 题目描述 给定一个已排序的链表的头 headheadhead , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。 示例 1: 输入:head [1,2,3,…...
tensorflow gpu环境安装
查看本电脑支持的最高cuda版本:nvidia-smi在~/.condarc修改conda 源:channels:- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/- https://mirrors.tuna.tsinghua.edu.cn/…...
如何在现实场景中随心放置AR虚拟对象?
随着AR的发展和电子设备的普及,人们在生活中使用AR技术的门槛降低,比如对于不方便测量的物体使用AR测量,方便又准确;遇到陌生的路段使用AR导航,清楚又便捷;网购时拿不准的物品使用AR购物,体验更…...
操作系统-处理机调度
1.处理机调度的概念、层次1.1调度的基本概念制定某种规则来决定处理任务的顺序。1.2调度的三个层次高级调度(作业调度)中级调度(内存调度)进程的挂起态与七状态模型低级调度(进程调度)小结2.进程调度的时机…...
手机截图如何提取文字?
在当今信息爆炸的时代,图文并茂已经成为了一个广告宣传的常用方式。然而,图片中的文字信息往往难以获取,尤其对于那些需要快速获取信息的人们来说,阅读图片中的文字会是一项繁琐且费时的任务。现在,我们有一个好消息要…...
vue中复制内容
vue中复制内容vue2vue-clipboard2依赖项在main.js引入使用vue3vue-clipboard3依赖项引入使用更新于:2023-02-15vue2vue-clipboard2 依赖项 “vue”: “^2.6.11” “vue-clipboard2”: “^0.3.1” 在main.js引入 import VueClipboard from vue-clipboard2 Vue.us…...
MySQL CAST()函数用法
一、语法 expr:源数据,如字符串’China’。type:目标数据类型,例如CHAR。 cast(expr AS type)二、命令说明 将任何类型的值转换为具有指定类型的值。 CAST()函数通常用于返回具有指定类型的值,以便在WHEREÿ…...
【测试工程师面试】详细记录 自己的一次面试
【测试工程师面试】详细记录 自己的一次面试 目录:导读 Linux基础 Oracle基础 编程基础 测试的基础 面试的问题 扯闲话部分: 10点刚到,先进行笔试,笔试的题目很基础,涉及到linux,涉及到oracle数据库…...
Elasticsearch 安装(二)
目录前言一、Linux 安装1、下载安装包⑴、选择需要的安装包⑵、下载解压到安装目录2、查看解压后目录结构3、启动 Elasticsearch⑴、正常启动流程⑵、启动过程遇到的问题①、启动报错②、创建运行 Elasticsearch 的用户,启动成功,但无法访问③、停止Elas…...
Java基础:异常与错误(ExceptionError)
1 缘起 某天上网冲浪时,偶然看到一个问题,说Java的Error和Exception有什么区别? 一句话:不知道。并不能很清晰地描述出个中区别。 当然,曾经也看过Throwable相关的知识,但是,并没有通过源码及注…...
VAmPI:一个包含了OWASP Top10漏洞的REST API安全学习平台
关于VAmPI VAmPI是一个包含了OWASP Top10漏洞的REST API安全学习平台,该平台基于Flask开发,该工具的主要目的是通过一个易受攻击的API来评估针对API安全检测工具的有效性,并帮助广大研究人员学习和了解API安全。 功能介绍 1、基于OWASP Top…...
springboot(6)之前端传递参数的方式 普通 集合 数组
实体类传递 首先我们在后端定义一个实体类,通过lombok插件重写 有参 无参 get set toString 方法, 然后前端发送数据,后端就会自动收到,然后属性填写 后端代码如下 AllArgsConstructor Data NoArgsConstructor public class role …...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
