Tio Boot DocsTio Boot Docs
Home
  • java-db
  • api-table
  • Enjoy
  • Tio Boot Admin
  • ai_agent
  • translator
  • knowlege_base
  • ai-search
  • 案例
Abount
  • Github
  • Gitee
Home
  • java-db
  • api-table
  • Enjoy
  • Tio Boot Admin
  • ai_agent
  • translator
  • knowlege_base
  • ai-search
  • 案例
Abount
  • Github
  • Gitee
  • 01_tio-boot 简介

    • tio-boot:新一代高性能 Java Web 开发框架
    • tio-boot 入门示例
    • Tio-Boot 配置 : 现代化的配置方案
    • tio-boot 整合 Logback
    • tio-boot 整合 hotswap-classloader 实现热加载
    • 自行编译 tio-boot
    • 最新版本
    • 开发规范
  • 02_部署

    • 使用 Maven Profile 实现分环境打包 tio-boot 项目
    • Maven 项目配置详解:依赖与 Profiles 配置
    • tio-boot 打包成 FastJar
    • 使用 GraalVM 构建 tio-boot Native 程序
    • 使用 Docker 部署 tio-boot
    • 部署到 Fly.io
    • 部署到 AWS Lambda
    • 到阿里云云函数
    • 使用 Deploy 工具部署
    • 胖包与瘦包的打包与部署
    • 使用 Jenkins 部署 Tio-Boot 项目
    • 使用 Nginx 反向代理 Tio-Boot
    • 使用 Supervisor 管理 Java 应用
  • 03_配置

    • 配置参数
    • 服务器监听器
    • 内置缓存系统 AbsCache
    • 使用 Redis 作为内部 Cache
    • 静态文件处理器
    • 基于域名的静态资源隔离
    • DecodeExceptionHandler
  • 04_原理

    • 生命周期
    • 请求处理流程
    • 重要的类
  • 05_json

    • Json
    • 接受 JSON 和响应 JSON
    • 响应实体类
  • 06_web

    • 概述
    • 文件上传
    • 接收请求参数
    • 接收日期参数
    • 接收数组参数
    • 返回字符串
    • 返回文本数据
    • 返回网页
    • 请求和响应字节
    • 文件下载
    • 返回视频文件并支持断点续传
    • http Session
    • Cookie
    • HttpRequest
    • HttpResponse
    • Resps
    • RespBodyVo
    • /zh/06_web/19.html
    • 全局异常处理器
    • 异步
    • 动态 返回 CSS 实现
    • 返回图片
    • Transfer-Encoding: chunked 实时音频播放
    • Server-Sent Events (SSE)
    • 接口访问统计
    • 接口请求和响应数据记录
    • 自定义 Handler 转发请求
    • 使用 HttpForwardHandler 转发所有请求
    • 跨域
    • 添加 Controller
    • 常用工具类
    • HTTP Basic 认证
    • WebJars
    • JProtobuf
  • 07_validate

    • 数据紧校验规范
    • 参数校验
  • 08_websocket

    • 使用 tio-boot 搭建 WebSocket 服务
    • WebSocket 聊天室项目示例
  • 09_java-db

    • java‑db
    • 操作数据库入门示例
    • SQL 模板
    • 数据源配置与使用
    • ActiveRecord
    • Model
    • 生成器与 Model
    • Db 工具类
    • 批量操作
    • 数据库事务处理
    • Cache 缓存
    • Dialect 多数据库支持
    • 表关联操作
    • 复合主键
    • Oracle 支持
    • Enjoy SQL 模板
    • Java-DB 整合 Enjoy 模板最佳实践
    • 多数据源支持
    • 独立使用 ActiveRecord
    • 调用存储过程
    • java-db 整合 Guava 的 Striped 锁优化
    • 生成 SQL
    • 通过实体类操作数据库
    • java-db 读写分离
    • Spring Boot 整合 Java-DB
    • like 查询
    • 常用操作示例
    • Druid 监控集成指南
    • SQL 统计
  • 10_api-table

    • ApiTable 概述
    • 使用 ApiTable 连接 SQLite
    • 使用 ApiTable 连接 Mysql
    • 使用 ApiTable 连接 Postgres
    • 使用 ApiTable 连接 TDEngine
    • 使用 api-table 连接 oracle
    • 使用 api-table 连接 mysql and tdengine 多数据源
    • EasyExcel 导出
    • EasyExcel 导入
    • TQL(Table SQL)前端输入规范
    • ApiTable 实现增删改查
    • 数组类型
    • 单独使用 ApiTable
  • 11_aop

    • JFinal-aop
    • Aop 工具类
    • 配置
    • 配置
    • 独立使用 JFinal Aop
    • @AImport
    • 原理解析
  • 12_cache

    • Caffine
    • Jedis-redis
    • hutool RedisDS
    • Redisson
    • Caffeine and redis
    • CacheUtils 工具类
    • 使用 CacheUtils 整合 caffeine 和 redis 实现的两级缓存
    • 使用 java-db 整合 ehcache
    • 使用 java-db 整合 redis
    • Java DB Redis 相关 Api
    • redis 使用示例
  • 13_认证和权限

    • hutool-JWT
    • FixedTokenInterceptor
    • 使用内置 TokenManager 实现登录
    • 用户系统
    • 重置密码
    • 匿名登录
    • Google 登录
    • 权限校验注解
    • Sa-Token
    • sa-token 登录注册
    • StpUtil.isLogin() 源码解析
    • 短信登录
    • 移动端微信登录实现指南
    • 移动端重置密码
  • 14_i18n

    • i18n
  • 15_enjoy

    • tio-boot 整合 Enjoy 模版引擎文档
    • 引擎配置
    • 表达式
    • 指令
    • 注释
    • 原样输出
    • Shared Method 扩展
    • Shared Object 扩展
    • Extension Method 扩展
    • Spring boot 整合
    • 独立使用 Enjoy
    • tio-boot enjoy 自定义指令 localeDate
    • PromptEngine
    • Enjoy 入门示例-擎渲染大模型请求体
    • Enjoy 使用示例
  • 16_定时任务

    • Quartz 定时任务集成指南
    • 分布式定时任务 xxl-jb
    • cron4j 使用指南
  • 17_tests

    • TioBootTest 类
  • 18_tio

    • TioBootServer
    • tio-core
    • 内置 TCP 处理器
    • 独立启动 UDPServer
    • 使用内置 UDPServer
    • t-io 消息处理流程
    • tio-运行原理详解
    • TioConfig
    • ChannelContext
    • Tio 工具类
    • 业务数据绑定
    • 业务数据解绑
    • 发送数据
    • 关闭连接
    • Packet
    • 监控: 心跳
    • 监控: 客户端的流量数据
    • 监控: 单条 TCP 连接的流量数据
    • 监控: 端口的流量数据
    • 单条通道统计: ChannelStat
    • 所有通道统计: GroupStat
    • 资源共享
    • 成员排序
    • ssl
    • DecodeRunnable
    • 使用 AsynchronousSocketChannel 响应数据
    • 拉黑 IP
    • 深入解析 Tio 源码:构建高性能 Java 网络应用
  • 19_aio

    • ByteBuffer
    • AIO HTTP 服务器
    • 自定义和线程池和池化 ByteBuffer
    • AioHttpServer 应用示例 IP 属地查询
    • 手写 AIO Http 服务器
  • 20_netty

    • Netty TCP Server
    • Netty Web Socket Server
    • 使用 protoc 生成 Java 包文件
    • Netty WebSocket Server 二进制数据传输
    • Netty 组件详解
  • 21_netty-boot

    • Netty-Boot
    • 原理解析
    • 整合 Hot Reload
    • 整合 数据库
    • 整合 Redis
    • 整合 Elasticsearch
    • 整合 Dubbo
    • Listener
    • 文件上传
    • 拦截器
    • Spring Boot 整合 Netty-Boot
    • SSL 配置指南
    • ChannelInitializer
    • Reserve
  • 22_MQ

    • Mica-mqtt
    • EMQX
    • Disruptor
  • 23_tio-utils

    • tio-utils
    • HttpUtils
    • Notification
    • 邮箱
    • JSON
    • 读取文件
    • Base64
    • 上传和下载
    • Http
    • Telegram
    • RsaUtils
    • EnvUtils 使用文档
    • 系统监控
    • 毫秒并发 ID (MCID) 生成方案
  • 24_tio-http-server

    • 使用 Tio-Http-Server 搭建简单的 HTTP 服务
    • tio-boot 添加 HttpRequestHandler
    • 在 Android 上使用 tio-boot 运行 HTTP 服务
    • tio-http-server-native
    • handler 常用操作
  • 25_tio-websocket

    • WebSocket 服务器
    • WebSocket Client
  • 26_tio-im

    • 通讯协议文档
    • ChatPacket.proto 文档
    • java protobuf
    • 数据表设计
    • 创建工程
    • 登录
    • 历史消息
    • 发消息
  • 27_mybatis

    • Tio-Boot 整合 MyBatis
    • 使用配置类方式整合 MyBatis
    • 整合数据源
    • 使用 mybatis-plus 整合 tdengine
    • 整合 mybatis-plus
  • 28_mongodb

    • tio-boot 使用 mongo-java-driver 操作 mongodb
  • 29_elastic-search

    • Elasticsearch
    • JavaDB 整合 ElasticSearch
    • Elastic 工具类使用指南
    • Elastic-search 注意事项
    • ES 课程示例文档
  • 30_magic-script

    • tio-boot 整合 magic-script
  • 31_groovy

    • tio-boot 整合 Groovy
  • 32_firebase

    • 整合 google firebase
    • Firebase Storage
    • Firebase Authentication
    • 使用 Firebase Admin SDK 进行匿名用户管理与自定义状态标记
    • 导出用户
    • 注册回调
    • 登录注册
  • 33_文件存储

    • 文件上传数据表
    • 本地存储
    • 使用 AWS S3 存储文件并整合到 Tio-Boot 项目中
    • 存储文件到 腾讯 COS
  • 34_spider

    • jsoup
    • 爬取 z-lib.io 数据
    • 整合 WebMagic
    • WebMagic 示例:爬取学校课程数据
    • Playwright
    • Flexmark (Markdown 处理器)
    • tio-boot 整合 Playwright
    • 缓存网页数据
  • 36_integration_thirty_party

    • tio-boot 整合 okhttp
    • 整合 GrpahQL
    • 集成 Mailjet
    • 整合 ip2region
    • 整合 GeoLite 离线库
    • 整合 Lark 机器人指南
    • 集成 Lark Mail 实现邮件发送
    • Thymeleaf
    • Swagger
    • Clerk 验证
  • 37_dubbo

    • 概述
    • dubbo 2.6.0
    • dubbo 2.6.0 调用过程
    • dubbo 3.2.0
  • 38_spring

    • Spring Boot Web 整合 Tio Boot
    • spring-boot-starter-webflux 整合 tio-boot
    • Tio Boot 整合 Spring Boot Starter
    • Tio Boot 整合 Spring Boot Starter Data Redis 指南
  • 39_spring-cloud

    • tio-boot spring-cloud
  • 40_mysql

    • 使用 Docker 运行 MySQL
    • /zh/42_mysql/02.html
  • 41_postgresql

    • PostgreSQL 安装
    • PostgreSQL 主键自增
    • PostgreSQL 日期类型
    • Postgresql 金融类型
    • PostgreSQL 数组类型
    • PostgreSQL 全文检索
    • PostgreSQL 查询优化
    • 获取字段类型
    • PostgreSQL 向量
    • PostgreSQL 优化向量查询
    • PostgreSQL 其他
  • 43_oceanbase

    • 快速体验 OceanBase 社区版
    • 快速上手 OceanBase 数据库单机部署与管理
    • 诊断集群性能
    • 优化 SQL 性能指南
    • /zh/43_oceanbase/05.html
  • 50_media

    • JAVE 提取视频中的声音
    • Jave 提取视频中的图片
    • /zh/50_media/03.html
  • 51_asr

    • Whisper-JNI
  • 54_native-media

    • java-native-media
    • JNI 入门示例
    • mp3 拆分
    • mp4 转 mp3
    • 使用 libmp3lame 实现高质量 MP3 编码
    • Linux 编译
    • macOS 编译
    • 从 JAR 包中加载本地库文件
    • 支持的音频和视频格式
    • 任意格式转为 mp3
    • 通用格式转换
    • 通用格式拆分
    • 视频合并
    • VideoToHLS
    • split_video_to_hls 支持其他语言
    • 持久化 HLS 会话
  • 55_telegram4j

    • 数据库设计
    • /zh/55_telegram4j/02.html
    • 基于 MTProto 协议开发 Telegram 翻译机器人
    • 过滤旧消息
    • 保存机器人消息
    • 定时推送
    • 增加命令菜单
    • 使用 telegram-Client
    • 使用自定义 StoreLayout
    • 延迟测试
    • Reactor 错误处理
    • Telegram4J 常见错误处理指南
  • 56_telegram-bots

    • TelegramBots 入门指南
    • 使用工具库 telegram-bot-base 开发翻译机器人
  • 60_LLM

    • 简介
    • AI 问答
    • /zh/60_LLM/03.html
    • /zh/60_LLM/04.html
    • 增强检索(RAG)
    • 结构化数据检索
    • 搜索+AI
    • 集成第三方 API
    • 后置处理
    • 推荐问题生成
    • 连接代码执行器
    • 避免 GPT 混乱
    • /zh/60_LLM/13.html
  • 61_ai_agent

    • 数据库设计
    • 示例问题管理
    • 会话管理
    • 历史记录
    • 对接 Perplexity API
    • 意图识别与生成提示词
    • 智能问答模块设计与实现
    • 文件上传与解析文档
    • 翻译
    • 名人搜索功能实现
    • Ai studio gemini youbue 问答使用说明
    • 自建 YouTube 字幕问答系统
    • 自建 获取 youtube 字幕服务
    • 通用搜索
    • /zh/61_ai_agent/15.html
    • 16
    • 17
    • 18
    • 在 tio-boot 应用中整合 ai-agent
    • 16
  • 62_translator

    • 简介
  • 63_knowlege_base

    • 数据库设计
    • 用户登录实现
    • 模型管理
    • 知识库管理
    • 文档拆分
    • 片段向量
    • 命中测试
    • 文档管理
    • 片段管理
    • 问题管理
    • 应用管理
    • 向量检索
    • 推理问答
    • 问答模块
    • 统计分析
    • 用户管理
    • api 管理
    • 存储文件到 S3
    • 文档解析优化
    • 片段汇总
    • 段落分块与检索
    • 多文档解析
    • 对话日志
    • 检索性能优化
    • Milvus
    • 文档解析方案和费用对比
    • 离线运行向量模型
  • 64_ai-search

    • ai-search 项目简介
    • ai-search 数据库文档
    • ai-search SearxNG 搜索引擎
    • ai-search Jina Reader API
    • ai-search Jina Search API
    • ai-search 搜索、重排与读取内容
    • ai-search PDF 文件处理
    • ai-search 推理问答
    • Google Custom Search JSON API
    • ai-search 意图识别
    • ai-search 问题重写
    • ai-search 系统 API 接口 WebSocket 版本
    • ai-search 搜索代码实现 WebSocket 版本
    • ai-search 生成建议问
    • ai-search 生成问题标题
    • ai-search 历史记录
    • Discover API
    • 翻译
    • Tavily Search API 文档
    • 对接 Tavily Search
    • 火山引擎 DeepSeek
    • 对接 火山引擎 DeepSeek
    • ai-search 搜索代码实现 SSE 版本
    • jar 包部署
    • Docker 部署
    • 爬取一个静态网站的所有数据
    • 网页数据预处理
    • 网页数据检索与问答流程整合
  • 65_java-linux

    • Java 执行 python 代码
    • 通过大模型执行 Python 代码
    • MCP 协议
    • Cline 提示词
    • Cline 提示词-中文版本
  • 66_manim

    • 简介
    • Manim 开发环境搭建
    • 生成场景提示词
    • 生成代码
    • 完整脚本示例
    • 语音合成系统
    • Fish.audio TTS 接口说明文档与 Java 客户端封装
    • 整合 fishaudio 到 java-uni-ai-server 项目
    • 执行 Python (Manim) 代码
    • 使用 SSE 流式传输生成进度的实现文档
    • 整合全流程完整文档
    • HLS 动态推流技术文档
    • manim 分场景生成代码
    • 分场景运行代码及流式播放支持
    • 分场景业务端完整实现流程
    • Maiim布局管理器
    • 仅仅生成场景代码
    • 使用 modal 运行 manim 代码
    • Python 使用 Modal GPU 加速渲染
    • Modal 平台 GPU 环境下运行 Manim
    • Modal Manim OpenGL 安装与使用
    • 优化 GPU 加速
    • 生成视频封面流程
    • Java 调用 manim 命令 执行代码 生成封面
    • Manim 图像生成服务客户端文档
    • manim render help
    • 显示 中文公式
    • manimgl
    • EGL
    • /zh/66_manim/30.html
    • /zh/66_manim/31.html
    • 成本核算
    • /zh/66_manim/33.html
  • 70_tio-boot-admin

    • 入门指南
    • 初始化数据
    • token 存储
    • 与前端集成
    • 文件上传
    • 网络请求
    • 图片管理
    • /zh/70_tio-boot-admin/08.html
    • Word 管理
    • PDF 管理
    • 文章管理
    • 富文本编辑器
  • 71_tio-boot

    • /zh/71_tio-boot/01.html
    • Swagger 整合到 Tio-Boot 中的指南
    • HTTP/1.1 Pipelining 性能测试报告
  • 80_性能测试

    • 压力测试 - tio-http-serer
    • 压力测试 - tio-boot
    • 压力测试 - tio-boot-native
    • 压力测试 - netty-boot
    • 性能测试对比
    • TechEmpower FrameworkBenchmarks
    • 压力测试 - tio-boot 12 C 32G
  • 99_案例

    • 封装 IP 查询服务
    • tio-boot 案例 - 全局异常捕获与企业微信群通知
    • tio-boot 案例 - 文件上传和下载
    • tio-boot 案例 - 整合 ant design pro 增删改查
    • tio-boot 案例 - 流失响应
    • tio-boot 案例 - 增强检索
    • tio-boot 案例 - 整合 function call
    • tio-boot 案例 - 定时任务 监控 PostgreSQL、Redis 和 Elasticsearch
    • Tio-Boot 案例:使用 SQLite 整合到登录注册系统
    • tio-boot 案例 - 执行 shell 命令

表达式

Enjoy Template Engine 表达式规则设计在总体上符合 java 表达式规则,仅仅针对模板引擎的特征进行极其少量的符合直觉的有利于开发体验的扩展。

对于表达式的使用,再次强调一个关键点:表达式与 Java 是直接打通的。掌握了这个关键点立即就掌握了模板引擎 90%的用法。如下是代码示例:

123 + "abc"
"abcd".substring(0, 1)
userList.get(0).getName()

以上代码第一、第二行,与 Java 表达式的用法完全一样。第三行代码中,假定 userList 中有 User 对象,并且 User 具有 getName()方法,只要知道变量的类型,就可以像使用 Java 表达式一样调用对象的方法。

1、与 java 规则基本相同的表达式

  • 算术运算: + - * / % ++ --
  • 比较运算: > >= < <= == != (基本用法相同,后面会介绍增强部分)
  • 逻辑运算: ! && ||
  • 三元表达式: ? :
  • Null 值常量: null
  • 字符串常量: "jfinal club"
  • 布尔常量:true false
  • 数字常量: 123 456F 789L 0.1D 0.2E10
  • 数组存取:array[i](Map 被增强为额外支持 map[key]的方式取值)
  • 属性取值:object.field(Map 被增强为额外支持 map.key 的方式取值)
  • 方法调用:object.method(p1, p2…, pn) (支持可变参数)

逗号表达式:123, 1>2, null, "abc", 3+6 (逗号表达式的值为最后一个表达式的值)

小技巧:如果从 java 端往 map 中传入一个 key 为中文的值,可以通过 map["中文"] 的方式去访问到,而不能用 "map.中文" 访问,因为引擎会将之优先当成是 object.field 的访问形式,而 object.field 形式默认不支持中文,当然也可以通过 Engine.setChineseExpression(true) 配置来支持。

三元表达式示例

<b>车队类型:</b>#(chat_type=="channel"?"频道":"群组")

2、属性访问

由于模板引擎的属性取值表达式极为常用,所以对其在用户体验上进行了符合直觉的扩展,field 表达式取值优先次序,以 user.name 为例:

  • 如果 user.getName() 存在,则优先调用

  • 如果 user 具有 public 修饰过的 name 属性,则取 user.name 属性值(注意:jfinal 4.0 之前这条规则的优先级最低)

  • 如果 user 为 Model 子类,则调用 user.get("name")

  • 如果 user 为 Record,则调用 user.get("name")

  • 如果 user 为 Map,则调用 user.get("name")

此外,还支持数组的 length 长度访问:array.length,与 java 语言一样

最后,属性访问表达式还可以通过 FieldGetter 抽象类扩展,具体方法参考 com.jfinal.template.expr.ast.FieldGetters,这个类中已经给出了多个默认实现类,以下配置将支持 user.girl 表达式去调用 user 对象的 boolean isGirl() 方法:

Engine.addFieldGetterToFirst(new com.jfinal.template.expr.ast.FieldGetters.IsMethodFieldGetter());

3、方法调用

模板引擎被设计成与 java 直接打通,可以在模板中直接调用对象上的任何 public 方法,使用规则与 java 中调用方式保持一致,以下代码示例:

#("ABCDE".substring(0, 3))
#(girl.getAge())
#(list.size())
#(map.get(key))

以上第一行代码调用了 String 对象上的 substring(0, 3)方法输出值为 "ABC"。第二行代码在 girl 对象拥有 getAge()方法时可调用。第三行代码假定 map 为一个 Map 类型时可调用其 get(...) 方法。

简单来说:模板表达式中可以直接调用对象所拥有的 public 方法,方法调用支持可变参数,例如支持这种方法被调用:obj.find(String sql, Object … args)。

对象方法调用与 java 直接打通式设计,学习成本为 0、与 java 交互极其方便、并且立即拥有了非常强大的扩展机制。

4、静态属性访问

自 jfinal 5.0.2 开始,该表达式默认 "未启用",启用需要添加如下配置:

engine.setStaticFieldExpression(true);

在模板中通常要访问 java 代码中定义的静态变量、静态常量,以下是代码示例:

#if(x.status == com.demo.common.model.Account::STATUS_LOCK_ID)
   <span>(账号已锁定)</span>
#end

如上所示,通过类名加双冒号再加静态属性名即为静态属性访问表达式,上例中静态属性在 java 代码中是一个 int 数值,通过这种方式可以避免在模板中使用具体的常量值,从而有利于代码重构。

由于静态属性访问需要包名前缀,代码显得比较长,在实际使用时如果多次用到同一个值,可以用 #set(STATUS_LOCK_ID = ...) 指令将常量值先赋给一个变量,可以节省一定的代码。

注意,这里的属性必须是 public static 修饰过的才可以被访问。此外,这里的静态属性并非要求为 final 修饰。

如果某个静态属性要被经常使用,建议通过 addSharedObject(...) 将其配置成共享对象,然后通过 field 表达式来引用,从而节省代码,例如先配置 shared object:

public void configEngine(Engine me) {
    me.addSharedObject("Account", new Account());
}

然后在模板中就可以使用 field 表达式来代替原有的静态属性访问表达式了:

#if(x.status == Account.STATUS_LOCK_ID)
   <span>(账号已锁定)</span>
#end

5、静态方法调用

自 jfinal 5.0.2 开始,该表达式默认 "未启用",启用需要添加如下配置:

engine.setStaticMethodExpression(true);

Enjoy Template Engine 可以以非常简单的方式调用静态方法,以下是代码示例:

#if(com.jfinal.kit.StrKit::isBlank(title))
   ....
#end

使用方式与前面的静态属性访问保持一致,仅仅是将静态属性名换成静态方法名,并且后面多一对小括号与参数:类名 + :: + 方法名(参数)。静态方法调用支持可变参数。与静态属性相同,被调用的方法需要使用 public static 修饰才可访问。

如果觉得类名前方的包名书写很麻烦,可以使用后续即将介绍的 me.addSharedMethod(…)方法将类中的方法添加为共享方法,调用的时候直接使用方法名即可,连类名都不再需要。

此外,还可以调用静态属性上的方法,以下是代码示例:

(com.jfinal.MyKit::me).method(paras)

上面代码中需要先用一对小扩号将静态属性取值表达式扩起来,然后再去调用它的方法,小括号在此仅是为了改变表达式的优先级。

6、可选链操作符 ?. (5.0.0 新增功能)

jfinal 5.0.0 版本新增了 optional chain 操作符,使用方式如下:

# 当 article 为 null 时不对 title 进行取值,而是直接返回 null
article?.title

# 可用于方法调用
article?.getTitle()

# 可级联操作
page?.list?.size()

# 可用在方法调回之后,以下代码在 getList() 返回 null 时可避免抛出异常
page?.getList()?.size()

在对某个对象进行 field 取值或者方法调用,如果该对象可能为 null,那么可以使用该操作符,避免抛出异常。注意该操作符在对象为 null 时始终返回 null,如果需要返回其它默认值,可以使用下一小节的 "空合并安全取值调用操作符"。

7、空合并安全取值调用操作符

Enjoy Template Engine 引入了 swift 与 C#语言中的空合操作符,并在其基础之上进行了极为自然的扩展,该表达式符号为两个紧靠的问号:??。代码示例:

seoTitle ?? "JFinal 社区"
object.field ??
object.method() ??

以上第一行代码的功能与 swift 语言功能完全一样,也即在 seoTitle 值为 null 时整个表达式取后面表达式的值。而第二行代码表示对 object.field 进行空安全(Null Safe)属性取值,即在 object 为 null 时表达式不报异常,并且值为 null。

第三行代码与第二行代码类似,仅仅是属性取值变成了方法调用,并称之为空安全(Null Safe)方法调用,表达式在 object 为 null 时不报异常,其值也为 null。

当然,空合并与空安全可以极为自然地混合使用,如下是示例:

object.field ?? "默认值"
object.method() ?? value

以上代码中,第一行代码表示左侧 null safe 属性取值为 null 时,整个表达式的值为后方的字符串中的值,而第二行代码表示值为 null 时整个表达式取 value 这个变量中的值。

特别注意:?? 操作符的优先级高于数学计算运算符:+、-、*、/、%,低于单目运算符:!、++、--。强制改变优先级使用小括号即可。

例子:a.b ?? && expr 表达式中,其 a.b ?? 为一个整体被求值,因为 ?? 优先级高于数学计算运算符,而数学计算运算符又高于 && 运算符,进而推导出 ?? 优先级高于&&

8、单引号字符串

针对 Template Engine 经常用于 html 的应用场景,添加了单引号字符串支持,以下是代码示例:

<a href="/" class="#(menu == 'index' ? 'current' : 'normal')"
   首页
</a>

以上代码中的三元表达式中有三处使用了单引号字符串,好处是可以与最外层的双引号协同工作,也可以反过来,最外层用单引号字符串,而内层表达式用双引号字符串。

这个设计非常有利于在模板文件中已有的双引号或单引号内容之中书写字符串表达式。

9、相等与不等比较表达式增强

相等不等表达式 == 与 != 会对左右表达式进行 left.equals(right)比较操作,所以可以对字符串进行直接比较,如下所示:

#if(nickName == "james")
  ...
#end

注意:Controller.keepPara(…) 方法会将任何数据转换成 String 后传递到 view 层,所以原本可以用相等表达式比较的两个 Integer 型数据,在 keepPara(…)后变得不可比较,因为变为了 String 与 Integer 型的比较。解决方法见本章的 Extionsion Method 小节。

10、布尔表达式增强

布尔表达式在原有 java 基础之下进行了增强,可以减少代码输入量,具体规则自上而下优先应用如下列表:

  • null 返回 false

  • boolean 类型,原值返回

  • String、StringBuilder 等一切继承自 CharSequence 类的对象,返回 length > 0

  • 其它返回 true

以上规则可以减少模板中的代码量,以下是示例:

#if(user && user.id == x.userId)
  ...
#end

以上代码中的 user 表达式实质上代替了 java 表达式的 user != null 这种写法,减少了代码量。当然,上述表达式如果使用 ?? 运算符,还可以更加简单顺滑:if (user.id ?? == x.userId)

11、Map 定义表达式

Map 定义表达式的最实用场景是在调用方法或函数时提供极为灵活的参数传递方式,当方法或函数需要传递的参数名与数量不确定时极为有用,以下是基本用法:

#set(map = {k1:123, "k2":"abc", "k3":object})
#(map.k1)
#(map.k2)
#(map["k1"])
#(map["k2"])
#(map.get("k1"))

如上图所示,map 的定义使用一对大括号,每个元素以 key : value 的形式定义,多个元素之间用逗号分隔。

key 只允许是合法的 java 变量名标识符或者 String 常量值(jfinal 3.4 起将支持 int、long、float、double、boolean、null 等等常量值),注意:上例中使用了标识符 k1 而非 String 常量值 "k1" 只是为了书写时的便利,与字符串是等价的,并不会对标识符 k1 进行表达式求值。

上图中通过#set 指令将定义的变量赋值给了 map 变量,第二与第三行中以 object.field 的方式进行取值,第四第五行以 map[key] 的方式进行取值,第六行则是与 java 表达式打通式的用法。

特别注意:上例代码如果使用 map[k1] 来取值,则会对 k1 标识符先求值,得到的是 null,也即 map[k1] 相当于 map[null],因此上述代码中使用了 map["k1"] 这样的形式来取值。 此外,map 取值还支持在定义的同时来取值,如下所示:

#({1:'自买', 2:'跟买'}.get(1))
#({1:'自买', 2:'跟买'}[2])

### 与双问号符联合使用支持默认值
#({1:'自买', 2:'跟买'}.get(999) ?? '其它')
    上述 key 为 int 常量,自 jfinal 3.4 版本才开始支持。

### 12、数组定义表达式
    直接举例:

// 定义数组 array,并为元素赋默认值
#set(array = [123, "abc", true])

// 获取下标为 1 的值,输出为: "abc"
#(array[1])

// 将下标为 1 的元素赋值为 false,并输出
#(array[1] = false, array[1])

以上代码演示了数组的定义与初始化,以及数据获取与赋值。其中最后一行代码并没有使用 #set 指令,也就是说数组定义表达式可以脱离 #set 指令,以任意表达式为形式使用在任何指令内部(Map 定义表达式也一样可以)

数组定义表达式的初始化元素除了可以使用常量值以外,还可以使用任意的表达式,包括变量、方法调用返回值等等:

#set(array = [ 123, "abc", true, a && b || c, 1 + 2, obj.doIt(x) ])

13、范围数组定义表达式

直接举例:

#for(x : [1..10])
   #(x)
#end

上图中的表达式 [1..10] 定义了一个范围数组,其值为从 1 到 10 的整数数组,该表达式通常用于在开发前端页面时,模拟迭代输出多条静态数据,而又不必从后端读取数据。

此外,还支持递减的范围数组,例如:[10..1] 将定义一个从 10 到 1 的整数数组。上例中的#for 指令与#()输出指令后续会详细介绍。

14、逗号表达式

将多个表达式使用逗号分隔开来组合而成的表达式称为逗号表达式,逗号表达式整体求值的结果为最后一个表达式的值。例如:1+2, 3*4 这个逗号表达式的值为 12。

15、从 java 中去除的运算符

针对模板引擎的应用场景,去除了位运算符,避免开发者在模板引擎中表述过于复杂,保持模板引擎的应用初衷,同时也可以提升性能。

16、表达式总结

以上各小节介绍的表达式用法,主要是在 java 表达式规则之上做的有利于开发体验的精心扩展,你也可以先无视这些用法,而是直接当成是 java 表达式去使用,则可以免除掉上面的学习成本。

上述这些在 java 表达式规则基础上做的精心扩展,一是基于模板引擎的实际使用场景而添加,例如单引号字符串。二是对过于啰嗦的 java 语法的改进,例如字符串的比较 str == "james" 取代 str.equals("james"),所以是十分值得和必要的。

Edit this page
Last Updated:
Contributors: Tong Li
Prev
引擎配置
Next
指令