目录
1.实现自定义指令
2.自定义指令案例
2.1.无body的自定义指令
2.2.有body的自定义指令
3.套路总结
1.实现自定义指令
通过继承 Directive 并实现里面的必要方法即可以实现一个自定义指令,在使用前只需通过me.addDirective(“name”, MyDirective.class)方法 添加到模板引擎中即可在页面中根据 name 直接使用。
阅读源码,简单了解下 Directive 类。
/** * Directive 供用户继承并扩展自定义指令,具体用法可以参考 * com.jfinal.template.ext.directive 包下面的例子 */ public abstract class Directive extends Stat { /** * 传递给指令的表达式列表 * 1:表达式列表可通过 exprList.eval(scope) 以及 exprList.evalExprList(scope) 进行求值 * 2:使用赋值表达式可实现参数传递功能 * * * 例如:#render("_hot.html", title="热门新闻", list=newsList) * */ protected ExprList exprList; /** * 具有 #end 结束符的指令内部嵌套的所有内容,调用 stat.exec(env, scope, writer) * 即可执行指令内部嵌入所有指令与表达式,如果指令没有 #end 结束符,该属性无效 */ protected Stat stat; /** * 指令被解析时注入指令参数表达式列表,继承类可以通过覆盖此方法对参数长度和参数类型进行校验 */ public void setExprList(ExprList exprList) { this.exprList = exprList; } /** * 指令被解析时注入指令 body 内容,仅对于具有 #end 结束符的指令有效 */ public void setStat(Stat stat) { this.stat = stat; } }
2.自定义指令案例
指令可以分为无body的和有body的两种,同样自定义指令也有这两种,实现过程是一样的,只是个别方法稍有差别,我们分别进行两种自定义指令的演示。
案例代码讲解请移步至:JFinal从入门到实战视频教程【60集】
2.1.无body的自定义指令
目标:实现一个 strCut 指令,可以将字符串截取为指定的长度并添加省略号。
/** * #cutStr(string, length)两个参数分别是原字符串、要保留的字符串长度,默认拼接... * #cutStr(string, length, pattern)三个参数分别是原字符串、要保留的字符串长度、截取后拼接的内容 */ public class CutStrDirective extends Directive { /** * 设定多个属性,便于多个方法中使用 */ private Expr stringExpr;//第一个参数表达式 private Expr lengthExpr;//第二个参数表达式 private Expr patternExpr;//第三个参数表达式 private int paraNum;//参数个数 private String patternValue = "...";//默认的追加字符 /** * 解析参数列表:将各个参数赋值对应的属性,进行参数个数校验 */ public void setExprList(ExprList exprList) { paraNum = exprList.length(); if(paraNum<2 || paraNum>3){ //参数不对应时抛出异常,该异常是词法、语法异常 throw new ParseException("参数个数异常",location); } stringExpr = exprList.getExpr(0); lengthExpr = exprList.getExpr(1); if(paraNum == 3){ patternExpr = exprList.getExpr(2); } } /** * 执行指令: * 1.获取参数的具体值 * 2.执行内容输出 */ public void exec(Env env, Scope scope, Writer writer) { //获取参数的object值 Object stringObject = stringExpr.eval(scope); Object lengthObject = lengthExpr.eval(scope); Object patternObject = null; if(paraNum == 3){ patternObject = patternExpr.eval(scope); } //设定几个局部变量以便多处使用 String stringValue; Integer lengthValue; /** * 判断参数类型并进行实际值转换 * 当类型不符时排除异常,建议使用TemplateException运行异常 */ if(stringObject instanceof String){ stringValue = (String) stringObject; } else { throw new TemplateException("第一个参数必须是String类型", location); } if(lengthObject instanceof Integer){ lengthValue = (Integer) lengthObject; } else { throw new TemplateException("第二个参数必须是Integer/int类型", location); } if(paraNum == 3){ if(patternObject instanceof String){ patternValue = (String) patternObject; } else { throw new TemplateException("第三个参数必须是String类型", location); } } /** * 指令的逻辑处理 */ if(stringValue.length() > lengthValue){ stringValue = stringValue.substring(0, lengthValue); stringValue += patternValue; } /** * 实际输出 */ try { writer.write(stringValue); } catch (IOException e) { throw new TemplateException(e.getMessage(), location, e); } } }
2.2.有body的自定义指令
目标:实现一个 hotNews 自定义指令,可以获取指定条数的热点文章。
/** * #hotNews(8, "h") * <a href="">#(h)</a> * #end */ public class HotNewsDirective extends Directive { private Expr numsExpr; private Expr paraNameExpr; private int paraNum; private String paraNameValue = "h"; /** * 解析参数列表:将各个参数赋值对应的属性,进行参数个数校验 */ public void setExprList(ExprList exprList) { paraNum = exprList.length(); if(paraNum<1 || paraNum>2){ throw new ParseException("参数个数异常",location); } numsExpr = exprList.getFirstExpr();//获取第一个参数 if(paraNum == 2){ paraNameExpr = exprList.getLastExpr();//获取最后一个参数 } } /** * 执行指令: * 1.获取参数的具体值 * 2.执行内容输出 */ public void exec(Env env, Scope scope, Writer writer) { Object numsObject = numsExpr.eval(scope); Object paraNameObject = null; int numsValue = 0; if(numsObject instanceof Integer){ numsValue = (int) numsObject; } else { throw new TemplateException("第一个参数必须是int类型",location); } if(paraNum == 2){ paraNameObject = paraNameExpr.eval(scope); if(paraNameObject instanceof String){ paraNameValue = (String) paraNameObject; } else { throw new TemplateException("第一个参数必须是String类型", location); } } /** * 模拟数据库查询 */ List<String> news = new ArrayList<String>(); for(int i=0;i<numsValue;i++){ news.add("文章标题 " + i +" :"); } /** * 执行指令的body部分 */ for (int i = 0; i < news.size(); i++) { scope.set(paraNameValue, news.get(i)); stat.exec(env, scope, writer);//执行body部分内容 } } /** * 有body的自定义指令必须覆写这个方法并返回true */ public boolean hasEnd() { return true; } }
3.套路总结
必须继承 Directive 类;
设置几个属性用来接收指令的参数;
在 setExprList 中完成参数列表解析,并将指令的参数赋予我们准备的属性中;
在 exec 方法中进行指令输出,包括获取实际参数值、类型校验等;
如果是有 body 体的指令,必须覆写 hasEnd 方法并返回 true;
没有 body 的指令是通过 writer 写出内容,有 body 的指令通过 stat.exec 执行 body 部分的内容;
自定义指令中完全可以进行数据库交互等复杂操作,但是需要注意响应时间,不要让页面等待太久;
更多详细内容请移步至:JFinal从入门到实战视频教程【60集】
只要浏览一下 Directive 中的注释说明,再看看官方出的几个扩展的例子就能愉快地玩耍了:
com.jfinal.template.ext.directive