个人博客 地址:http://www.wenhaofan.com/article/20190304102258

平时在项目中使用短信模板 邮件模板以及 站内消息通知等文本模板一般都是通过手动的字符串拼接来完成,例如:"欢迎"+user.getName()+"加入俱乐部。"

然而这种方法不仅代码难看而且还不方便管理,因此为了更方便的在项目中管理使用这类文本模板,参考JFinal源码中的activerecord管理sql的代码来扩展了Enjoy的指令。

1.扩展代码

package com.autu.common.keys;

import com.jfinal.kit.StrKit;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope; /**
* KeysDirective
*/
public class KeysDirective extends Directive { static final String KEYS_DIRECTIVE="_KEYS_DIRECTIVE"; private String id; public void setExprList(ExprList exprList) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #keys directive can not be blank", location);
}
if (exprList.length() > 1) {
throw new ParseException("Only one parameter allowed for #keys directive", location);
}
Expr expr = exprList.getExpr(0);
if (expr instanceof Const && ((Const)expr).isStr()) {
} else {
throw new ParseException("The parameter of #keys directive must be String", location);
} this.id = ((Const)expr).getStr();
} public void exec(Env env, Scope scope, Writer writer) {
String beforeKey=(String)scope.get(KeysDirective.KEYS_DIRECTIVE); String key = StrKit.isBlank(beforeKey) ? id : beforeKey + "." + id;
scope.set(KEYS_DIRECTIVE, key); stat.exec(env, scope, writer); } public boolean hasEnd() {
return true;
}
}
package com.autu.common.keys;

import java.util.Map;

import com.jfinal.kit.StrKit;
import com.jfinal.template.Directive;
import com.jfinal.template.Env;
import com.jfinal.template.Template;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope; /**
* KeyDirective
*/
public class KeyDirective extends Directive { static final String KEY_DIRECTIVE="_KEY_DIRECTIVE"; private String id; public void setExprList(ExprList exprList) {
if (exprList.length() == 0) {
throw new ParseException("The parameter of #key directive can not be blank", location);
}
if (exprList.length() > 1) {
throw new ParseException("Only one parameter allowed for #key directive", location);
}
Expr expr = exprList.getExpr(0);
if (expr instanceof Const && ((Const)expr).isStr()) {
} else {
throw new ParseException("The parameter of #key directive must be String", location);
} this.id = ((Const)expr).getStr();
} @SuppressWarnings("unchecked")
public void exec(Env env, Scope scope, Writer writer) {
String nameSpace = (String)scope.get(KeysDirective.KEYS_DIRECTIVE);
String key = StrKit.isBlank(nameSpace) ? id : nameSpace + "." + id;
Map<String, Template> keyTemplateMap = (Map<String, Template>)scope.get(KeyKit.KEY_TEMPLATE_MAP_KEY);
if (keyTemplateMap.containsKey(key)) {
throw new ParseException("Key already exists with key : " + key, location);
} keyTemplateMap.put(key, new Template(env, stat));
} public boolean hasEnd() {
return true;
}
}
package com.autu.common.keys;

import com.jfinal.template.source.ISource;

/**
* 封装 key 模板源
*/
class KeySource { String file;
ISource source; KeySource(String file) {
this.file = file;
this.source = null;
} KeySource(ISource source) {
this.file = null;
this.source = source;
} boolean isFile() {
return file != null;
}
}
package com.autu.common.keys;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.jfinal.template.Engine;
import com.jfinal.template.Template;
import com.jfinal.template.source.ISource;
import com.jfinal.template.stat.ParseException; /**
* Email Kit
*/
public class KeyKit { static final String KEY_TEMPLATE_MAP_KEY = "_KEY_TEMPLATE_MAP_";
static final boolean DEFAULT_DEV_MODE=false;
public static final String MAIN_CONFIG_NAME = "keys";
public String configName;
private boolean devMode=false;
private Engine engine;
private List<KeySource> keySourceList = new ArrayList<KeySource>();
public Map<String, Template> keyTemplateMap; private static final Map<String,KeyKit> keyKitMap=new HashMap<>(); public static KeyKit use(String configName) {
return keyKitMap.get(configName);
} public static KeyKit use() {
return use(MAIN_CONFIG_NAME);
} private KeyKit(boolean devMode) {
this(MAIN_CONFIG_NAME, devMode);
} public static KeyKit load(String configName) { if(keyKitMap.containsKey(configName)) {
return keyKitMap.get(configName);
} return new KeyKit(configName,DEFAULT_DEV_MODE);
} public static KeyKit load(boolean devMode) {
if(keyKitMap.containsKey(MAIN_CONFIG_NAME)) {
return keyKitMap.get(MAIN_CONFIG_NAME);
}
return new KeyKit(devMode);
} public static KeyKit load(String configName, boolean devMode) {
if(keyKitMap.containsKey(configName)) {
return keyKitMap.get(configName);
}
return new KeyKit(configName, devMode);
} private KeyKit(String configName, boolean devMode) {
this.configName = configName;
this.devMode = devMode; if(keyKitMap.containsKey(configName)) {
throw new ParseException("Key already exists", null );
} engine = new Engine(configName);
engine.setDevMode(devMode);
engine.addDirective("key", KeyDirective.class);
engine.addDirective("keys", KeysDirective.class);
engine.addSharedMethod(new StrKit()); keyKitMap.put(configName, this);
} public KeyKit(String configName) {
this(configName, false);
} public Engine getEngine() {
return engine;
} public void setDevMode(boolean devMode) {
this.devMode = devMode;
engine.setDevMode(devMode);
} public KeyKit setBaseKeyTemplatePath(String baseKeyTemplatePath) {
engine.setBaseTemplatePath(baseKeyTemplatePath);
return this;
} public KeyKit addTemplate(String KeyTemplate) {
if (StrKit.isBlank(KeyTemplate)) {
throw new IllegalArgumentException("keyTemplate can not be blank");
}
keySourceList.add(new KeySource(KeyTemplate));
return this;
} public void addTemplate(ISource keyTemplate) {
if (keyTemplate == null) {
throw new IllegalArgumentException("keyTemplate can not be null");
}
keySourceList.add(new KeySource(keyTemplate));
} public synchronized KeyKit parseKeysTemplate() {
Map<String, Template> keyTemplateMap = new HashMap<String, Template>(512, 0.5F);
for (KeySource ss : keySourceList) {
Template template = ss.isFile() ? engine.getTemplate(ss.file) : engine.getTemplate(ss.source);
Map<Object, Object> data = new HashMap<Object, Object>();
data.put(KEY_TEMPLATE_MAP_KEY, keyTemplateMap);
template.renderToString(data);
}
this.keyTemplateMap = keyTemplateMap;
return this;
} private void reloadModifiedKeyTemplate() {
engine.removeAllTemplateCache(); // 去除 Engine 中的缓存,以免 get 出来后重新判断 isModified
parseKeysTemplate();
} private boolean isKeyTemplateModified() {
for (Template template : keyTemplateMap.values()) {
if (template.isModified()) {
return true;
}
}
return false;
} private Template getKeyTemplate(String key) {
Template template = keyTemplateMap.get(key);
if (template == null) { // 此 if 分支,处理起初没有定义,但后续不断追加 key 的情况
if (!devMode) {
return null;
}
if (isKeyTemplateModified()) {
synchronized (this) {
if (isKeyTemplateModified()) {
reloadModifiedKeyTemplate();
template = keyTemplateMap.get(key);
}
}
}
return template;
} if (devMode && template.isModified()) {
synchronized (this) {
template = keyTemplateMap.get(key);
if (template.isModified()) {
reloadModifiedKeyTemplate();
template = keyTemplateMap.get(key);
}
}
}
return template;
} /**
* 示例: 1:模板 定义 #key("key")
*
* #end
*
* 2:java 代码 getContent("key", Kv);
*/
public String getContent(String key, Kv kv) {
Template template = getKeyTemplate(key);
if (template == null) {
return null;
}
return template.renderToString(kv);
} public java.util.Set<java.util.Map.Entry<String, Template>> getKeyMapEntrySet() {
return keyTemplateMap.entrySet();
} public String toString() {
return "KeyTplKit for config : " + configName;
}
}

2.模板文件

3.1 all_emails.tpl

#keys("comment")
#include("innerKeys.tpl")
#end

3.2 comment.tpl

#key("comment_title")
[#(config.title)评论通知] Re:#(title)
#end

3.3 inner.tpl

#keys("inner")
#include("comment.tpl")
#end

3.创建管理工具

KeyKit.load(p.getBoolean("devMode", false))
.setBaseKeyTemplatePath(PathKit.getRootClassPath() + "/email")
.addTemplate("all_emails.tpl")
.parseKeysTemplate();

4.使用

   String keyContent=	KeyKit.use().getContent("comment.inner.comment_title",
Kv.by("config",new Config().setTitle("test")).set("title", "title1"));
System.out.println(keyContent);

输出结果

  [test评论通知] Re:title1

5.说明

此处演示的为多层嵌套keys的使用,单层和jfinal的sql管理一样使用

JFinal Enjoy指令扩展管理常用文本模板的更多相关文章

  1. T4文本模板转换过程

    T4文本模板转换过程将文本模板文件作为输入,生成一个新的文本文件作为输出. 例如,可以使用文本模板生成 Visual Basic 或 C# 代码,还可以生成 HTML 报告. 有三个组件参与这一过程: ...

  2. T4 文本模板编写准则

    如果要在 Visual Studio 中生成程序代码或其他应用程序资源,遵守以下一般准则可能非常有帮助. 它们并不是一成不变的规则. 设计时 T4 模板准则 设计时 T4 模板是在设计时在 Visua ...

  3. 编写 T4 文本模板

    文本模板由以下部件组成: 1)指令 - 控制模板处理方式的元素. 2)文本块 - 直接复制到输出的内容. 3)控制块 - 向文本插入可变值并控制文本的条件或重复部件的程序代码. 指令: 指令是控制模板 ...

  4. 使用 T4 文本模板生成设计时代码

      使用设计时 T4 文本模板,您可以在 Visual Studio 项目中生成程序代码和其他文件. 通常,您编写一些模板,以便它们根据来自模型的数据来改变所生成的代码. 模型是包含有关应用程序要求的 ...

  5. MVC开发T4代码生成之一----文本模板基础

    T4文本模板 T4全写为Text Template Transformation Toolkit,是一种编程辅助工具,用来使程序代码自(懒)动(猿)生(福)成(利)的工具.MVC开发中大量使用了T4模 ...

  6. T4文本模板

    <#...#> 可以包含语句 <#=...#>  用于表达式,提供“输出”操作 <#+ ...> 使用类功能控制块向文本模板添加方法.属性.字段,必须作为文件中最后 ...

  7. 第3章-Vue.js 指令扩展 和 todoList练习

    一.学习目标 了解Vue.js指令的实现原理 理解v-model指令的高级用法 能够使用Vue.js 指令完成 todoList 练习(重点+难点) 二.todoList练习效果展示 2.1.效果图展 ...

  8. T4模板之文本模板

    网址:https://docs.microsoft.com/en-us/visualstudio/modeling/design-time-code-generation-by-using-t4-te ...

  9. 【翻译】Scriban README 文本模板语言和.NET引擎

    scriban Scriban是一种快速.强大.安全和轻量级的文本模板语言和.NET引擎,具有解析liquid模板的兼容模式 Github https://github.com/lunet-io/sc ...

随机推荐

  1. Android 日期选择框 简洁常用

    效果 核心代码 >方法 /** * @description 选择日期弹出框 * @param listener 选择日期确定后执行的接口 * @param curDate 当前显示的日期 * ...

  2. jenkin docker node 自动化部署配置

    jenkins 安装必须插件 NodeJS Plugin Publish Over SSH 1:新建一个任务,选择构建一个自由风格的软件项目 2:源码管理选择Git 2.1 填写Git项目地址Repo ...

  3. 记一次Postgres CPU爆满故障

    问题描述 公司项目测试环境调用某些接口的时候,服务器立即崩溃,并一定时间内无法提供服务. 问题排查 服务器配置不够 第一反应是服务器需要升配啦,花钱解决一切!毕竟测试服务器配置确实不高,2CPU + ...

  4. [20200129]子光标不共享BIND_EQUIV_FAILURE.txt

    [20200129]子光标不共享BIND_EQUIV_FAILURE.txt --//生产系统再次遇到大量BIND_EQUIV_FAILURE原因导致子光标的情况.我看了我以前测试遇到的情况.--// ...

  5. linux 命令行下设置代理

    当linux 代理软件设置好后,我们需要设置命令行代理的连接方式,这样在命令行中的软件才能使用: 设置http/https代理: export https_proxy="127.0.0.1: ...

  6. [VB.NET Tips]创建匿名类型列表

    在调用一些Web API时经常要发送或接收一些数据,在构造Json时可能要创建一些类. 很多都是在调用相关方法才使用到这些类,那使用匿名类型是个不错的选择.如果要传些表结构数据时,就要创建List. ...

  7. Oracle列转行 参数动态传入iBatis使用示例

    Oracle行转列 参数动态传入iBatis使用示例 最近做了一个需求,需要获取工作流数据的各个节点的渠道数量信息,各渠道的费用信息~ 之前的需求是只需要获取渠道数据,所以做了渠道兼容,每个渠道数量的 ...

  8. SpringCloud之eureka注册中心入门

    eureka注册中心 一.基本概念 SpringCloud封装 了Netflix公司的eureka作为自己微服务的注册中心.这个注册中心和dubbo中的zookeeper很相似,简单来说,只要你可以将 ...

  9. Java基础之四、字符和字符串 异常处理

    字符和字符串是最常用的信息 1:char表示字符 字符常量-两个单引号中间的字符表示字符常量'A' 2:字符串和String 字符串常量-双引号中间的字符序列"Java" 字符串常 ...

  10. rsa special

    [ReSnAd] -- iqmp ipmq e,c,\(\phi(n)\) 题目: class Key: PRIVATE_INFO = ['P', 'Q', 'D', 'DmP1', 'DmQ1'] ...