个人站: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管理一样使用
对于一般的需求还有更简单的使用方式,例如,先将所有模板通过 #define 事先全部定义好,假定文件名为 template_define.txt:
#define comment()
[#(config.title)评论] Re:#(title)
#end
#define commnetReply()
[#(config.title)评论回复] Re:#(reply)
#end
然后通过 engine.addSharedFunction("template_define.txt") 将其添加为共享函数,然后就可以在任意地方使用了:
Engine engine = Engine.use();
Kv kv = kv.by("config", config).set(...);
String ret = engine.getTemplateByString("#@comment()").renderToString(kv);
System.out.print(ret);
好多好玩、简单、方便的用法呢