个人站: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);
好多好玩、简单、方便的用法呢