个人博客 地址: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管理一样使用