Enjoy自定义指令实现及套路总结

目录

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.套路总结

  1. 必须继承 Directive 类;

  2. 设置几个属性用来接收指令的参数;

  3. 在 setExprList 中完成参数列表解析,并将指令的参数赋予我们准备的属性中;

  4. 在 exec 方法中进行指令输出,包括获取实际参数值、类型校验等;

  5. 如果是有 body 体的指令,必须覆写 hasEnd 方法并返回 true;

  6. 没有 body 的指令是通过 writer 写出内容,有 body 的指令通过 stat.exec 执行 body 部分的内容;

  7. 自定义指令中完全可以进行数据库交互等复杂操作,但是需要注意响应时间,不要让页面等待太久;


更多详细内容请移步至:JFinal从入门到实战视频教程【60集】

评论区

JFinal

2020-08-22 13:51

总结得很对,指令开发就是这么玩的

只要浏览一下 Directive 中的注释说明,再看看官方出的几个扩展的例子就能愉快地玩耍了:
com.jfinal.template.ext.directive

SuperEric

2021-03-07 00:37

如果是有 body 体的指令,必须覆写 hasEnd 方法并返回 true; 这个太狠了,没注意指令一直报空指针,死活看不出哪里异常了。终于搞定了。❤

graphics

2022-01-22 13:08

@SuperEric 没注意太可怕了,呵呵

热门分享

扫码入社