目录
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