当前位置: 首页 > news >正文

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 还存在我的一些老项目中&#xff0c;比起前端框架&#xff0c;自有它的简便之处&…...

使用Benchto框架对Trino进行SQL性能对比测试

有时需要对魔改源码前后的不同版本Trino引擎进行性能对比测试&#xff0c;提前发现改造前后是否有性能变差或变好的现象&#xff0c;避免影响数据业务的日常查询任务性能。而Trino社区正好提供了一个性能测试对比框架&#xff1a;GitHub - trinodb/benchto: Framework for runn…...

Redis之哨兵模式

什么是哨兵模式&#xff1f; Sentinel(哨兵)是用于监控Redis集群中Master状态的工具&#xff0c;是Redis高可用解决方案&#xff0c;哨兵可以监视一个或者多个redis master服务&#xff0c;以及这些master服务的所有从服务。 某个master服务宕机后&#xff0c;会把这个master下…...

Selenium自动化测试Python二:WebDriver基础

欢迎阅读WebDriver基础讲义。本篇讲义将会重点介绍Selenium WebDriver的环境搭建和基本使用方法。 WebDriver环境搭建 Selenium WebDriver 又称为 Selenium2。 Selenium 1 WebDriver Selenium 2 WebDriver是主流Web应用自动化测试框架&#xff0c;具有清晰面向对象 API&…...

蓝桥杯模块学习17——AT24C02存储器(深夜学习——单片机)

一、硬件电路&#xff1a;1、引脚功能&#xff1a;&#xff08;1&#xff09;A0-A2&#xff1a;决定不同设备的地址码&#xff1a;&#xff08;2&#xff09;WP&#xff1a;写保护二、通讯方式&#xff08;IIC协议&#xff09;通讯方式与PCF8591相同&#xff0c;可参考以下文章…...

netty

Netty的介绍Netty是异步的&#xff08;指定回调处理&#xff09;、基于事件驱动的网络应用框架&#xff0c;用于快速开发高性能、高可靠性的网络IO程序。Netty本质是一个NIO框架&#xff0c;适用于服务器通讯相关的多种应用场景&#xff0c;分布式节点远程调用中Netty往往作为R…...

Django项目部署-uWSGI

Django项目部署-uWSGIDjango运维部署框架整体部署架构web服务器与web应用服务器的区别部署环境准备安装python3安装mariadb安装Django和相关模块Django托管服务器uWSGI使用uWSGI配置使用Django运维部署框架 整体部署架构 操作系统: Linux 。优势&#xff1a;生态系统丰富&…...

jhipster自动生成java代码的方法

一、前言 java springboot后台项目用到了jpa查询数据库&#xff0c;还用到了jhipster&#xff0c;这个东西可以自动生成基础的Controller、Service、Dao、JavaBean等相关代码&#xff0c;减少重复开发。 在此总结下使用方法。 二、jhipster自动生成java代码的方法 1.需要先…...

LeetCode 82. 删除排序链表中的重复元素 II

原题链接 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给定一个已排序的链表的头 headheadhead &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,…...

tensorflow gpu环境安装

查看本电脑支持的最高cuda版本&#xff1a;nvidia-smi在~/.condarc修改conda 源&#xff1a;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的发展和电子设备的普及&#xff0c;人们在生活中使用AR技术的门槛降低&#xff0c;比如对于不方便测量的物体使用AR测量&#xff0c;方便又准确&#xff1b;遇到陌生的路段使用AR导航&#xff0c;清楚又便捷&#xff1b;网购时拿不准的物品使用AR购物&#xff0c;体验更…...

操作系统-处理机调度

1.处理机调度的概念、层次1.1调度的基本概念制定某种规则来决定处理任务的顺序。1.2调度的三个层次高级调度&#xff08;作业调度&#xff09;中级调度&#xff08;内存调度&#xff09;进程的挂起态与七状态模型低级调度&#xff08;进程调度&#xff09;小结2.进程调度的时机…...

手机截图如何提取文字?

在当今信息爆炸的时代&#xff0c;图文并茂已经成为了一个广告宣传的常用方式。然而&#xff0c;图片中的文字信息往往难以获取&#xff0c;尤其对于那些需要快速获取信息的人们来说&#xff0c;阅读图片中的文字会是一项繁琐且费时的任务。现在&#xff0c;我们有一个好消息要…...

vue中复制内容

vue中复制内容vue2vue-clipboard2依赖项在main.js引入使用vue3vue-clipboard3依赖项引入使用更新于&#xff1a;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&#xff1a;源数据&#xff0c;如字符串’China’。type&#xff1a;目标数据类型&#xff0c;例如CHAR。 cast(expr AS type)二、命令说明 将任何类型的值转换为具有指定类型的值。 CAST()函数通常用于返回具有指定类型的值&#xff0c;以便在WHERE&#xff…...

【测试工程师面试】详细记录 自己的一次面试

【测试工程师面试】详细记录 自己的一次面试 目录&#xff1a;导读 Linux基础 Oracle基础 编程基础 测试的基础 面试的问题 扯闲话部分&#xff1a; 10点刚到&#xff0c;先进行笔试&#xff0c;笔试的题目很基础&#xff0c;涉及到linux&#xff0c;涉及到oracle数据库…...

Elasticsearch 安装(二)

目录前言一、Linux 安装1、下载安装包⑴、选择需要的安装包⑵、下载解压到安装目录2、查看解压后目录结构3、启动 Elasticsearch⑴、正常启动流程⑵、启动过程遇到的问题①、启动报错②、创建运行 Elasticsearch 的用户&#xff0c;启动成功&#xff0c;但无法访问③、停止Elas…...

Java基础:异常与错误(ExceptionError)

1 缘起 某天上网冲浪时&#xff0c;偶然看到一个问题&#xff0c;说Java的Error和Exception有什么区别&#xff1f; 一句话&#xff1a;不知道。并不能很清晰地描述出个中区别。 当然&#xff0c;曾经也看过Throwable相关的知识&#xff0c;但是&#xff0c;并没有通过源码及注…...

VAmPI:一个包含了OWASP Top10漏洞的REST API安全学习平台

关于VAmPI VAmPI是一个包含了OWASP Top10漏洞的REST API安全学习平台&#xff0c;该平台基于Flask开发&#xff0c;该工具的主要目的是通过一个易受攻击的API来评估针对API安全检测工具的有效性&#xff0c;并帮助广大研究人员学习和了解API安全。 功能介绍 1、基于OWASP Top…...

springboot(6)之前端传递参数的方式 普通 集合 数组

实体类传递 首先我们在后端定义一个实体类&#xff0c;通过lombok插件重写 有参 无参 get set toString 方法, 然后前端发送数据&#xff0c;后端就会自动收到&#xff0c;然后属性填写 后端代码如下 AllArgsConstructor Data NoArgsConstructor public class role …...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

基于服务器使用 apt 安装、配置 Nginx

&#x1f9fe; 一、查看可安装的 Nginx 版本 首先&#xff0c;你可以运行以下命令查看可用版本&#xff1a; apt-cache madison nginx-core输出示例&#xff1a; nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...