首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

制造一个自己的模板引擎(二)

2013-03-10 
打造一个自己的模板引擎(二)上一篇我们使用递归实现了一个模板渲染器,在模板结构复杂,嵌套比较多的时候,递

打造一个自己的模板引擎(二)
上一篇我们使用递归实现了一个模板渲染器,在模板结构复杂,嵌套比较多的时候,递归很容易把jvm的栈搞的很大,甚至崩溃。
因此接下来我们想办法避免使用递归。


静态语言在执行的时候,比如:if(my.say() == 'hello'){out.print("hello")}
用xml来表示:
<c:if test="${my.say() == 'hello'}">hello</c:if>
静态语言怎么执行呢?执行到if的时候,发现条件是个语句,并且还有个函数, 那么先把函数代码调入内存,如果有参数,把参数压入栈, 把程序指针指向该函数,
然后执行,执行完之后把结果压入栈,返回调用处,调用的地方从栈顶取出结果,然后继续执行。


我们可以考虑用类似的方式, 当遇到一个节点的时候,先调用doStartTag, 然后根据返回值决定是否要执行子节点,如果需要执行子节点,那么就把子节点压入栈,然后返回即可.
子节点执行完之后,需要调用doEndTag, 因此再把子节点压入栈之前先把当前节点压入栈,调用完doEndTag之后如果需要重复执行子节点,那么就再把自己压入栈,然后把子节点压入栈。
这样就可以不使用递归了,把递归处理变成了循环处理。而且很容易控制整个循环是否结束. 因为jstl的标签里面有个SKIP_PAGE的功能,也就是某个标签返回SKIP_PAGE的时候,需要立即终止整个渲染。


现在来说另外一个问题,先举一个例子:
<c:out escapeXml="true"><div><c:out value="123"></c:out></div></c:out>
对于这个,它的输出结果是什么呢?正确的输出结果是这样的:
&lt;div&gt;123&lt;/div&gt;
标签是怎么执行的呢?
首先第一层,第一个c:out没有var属性,也没有value属性,也没有defaultValue属性, 也就是说他要输出的是节点内的东西。
可以反编译一下tomcat或者resin的c:out的实现,发现他实现了BodyTagSupport. 再看一下源码,它还用了BodyContent.
BodyContent其实就是当前标签的内容,注意是渲染之后的内容。

也就是说当一个tag实现了BodyTag的时候,它就可以取得它的子标签渲染后的内容。这个功能的实现一开始不太好理解,其实很简单,看一下下面的代码:

public static void evaluate(final Node element, final PageContext pageContext){    Node node;    TagEntry tagEntry;    JspWriter out;    JspWriter jspWriter = pageContext.getOut();    Stack<TagEntry> stack = new Stack<TagEntry>();    stack.push(new TagEntry(element, 0));    ExpressionContext expressionContext = pageContext.getExpressionContext();    while((tagEntry = stack.poll()) != null)    {        out = pageContext.getOut();        node = tagEntry.getNode();        try        {            if(node.getNodeType() == NodeType.TEXT)            {                out.write(node.toString());                continue;            }            if(node.getNodeType() == NodeType.COMMENT)            {                out.write(node.toString());                continue;            }            if(node.getNodeType() == NodeType.EXPRESSION)            {                Object value = expressionContext.evaluate(node.toString());                if(value != null)                {                    out.write(value.toString());                }                continue;            }            if(tagEntry.getStatus() == 0 || tagEntry.getStatus() == 2)            {                int flag = startTag(stack, tagEntry, pageContext, expressionContext);                if(flag == Tag.SKIP_PAGE)                {                    break;                }            }            else            {                int flag = endTag(stack, tagEntry, pageContext);                if(flag == Tag.SKIP_PAGE)                {                    break;                }            }        }        catch(Exception e)        {            throw new RuntimeException(toString("Exception at ", node), e);        }    }    pageContext.setOut(jspWriter);}/** * @param stack * @param tagEntry * @param pageContext */private static int startTag(Stack<TagEntry> stack, TagEntry tagEntry, PageContext pageContext, ExpressionContext expressionContext){    int flag = 0;    Tag tag = tagEntry.getTag();    Node node = tagEntry.getNode();    if(tag == null)    {        tag = TagFactory.create(pageContext, node.getNodeName());        tagEntry.setTag(tag);        tag.setPageContext(pageContext);        if(tagEntry.getParent() != null)        {            tag.setParent(tagEntry.getParent().getTag());        }    }    int status = tagEntry.getStatus();    if(status == 0)    {        // create - doStartTag        TagUtil.setAttributes(tag, node.getAttributes(), expressionContext);        flag = tag.doStartTag();        tagEntry.setStartTagResult(flag);    }    else if(status == 1 || status == 2)    {        // to doEndTag        flag = tagEntry.getStartTagResult();    }    else    {        // illegal        throw new IllegalStateException("Tag State Error !");    }    tagEntry.setStatus(1);    stack.push(tagEntry);    if(flag == Tag.SKIP_PAGE)    {        return Tag.SKIP_PAGE;    }    if(flag != Tag.SKIP_BODY)    {        if(flag != Tag.EVAL_BODY_INCLUDE)        {            if(tag instanceof BodyTag)            {                BodyTag bodyTag = (BodyTag)tag;                BodyContent bodyContent = (BodyContent)(pageContext.pushBody());                bodyTag.setBodyContent(bodyContent);                bodyTag.doInitBody();            }        }        List<Node> childNodes = node.getChildNodes();        List<TagEntry> childEntrys = tagEntry.getChildNodes();        if(childNodes.size() > 0 && childEntrys.size() < 1)        {            for(int i = 0, size = childNodes.size(); i < size; i++)            {                TagEntry childTagEntry = new TagEntry(childNodes.get(i), 0);                childTagEntry.setParent(tagEntry);                childEntrys.add(childTagEntry);            }        }        if(childEntrys.size() > 0)        {            for(int i = childEntrys.size() - 1; i > -1; i--)            {                TagEntry childEntry = childEntrys.get(i);                childEntry.setStatus(0);                stack.push(childEntry);            }        }    }    return flag;}/** * @param stack * @param tagEntry * @param pageContext */private static int endTag(Stack<TagEntry> stack, TagEntry tagEntry, PageContext pageContext){    Tag tag = tagEntry.getTag();    IterationTag iterationTag = null;    if(tag instanceof IterationTag)    {        iterationTag = (IterationTag)tag;    }    if(iterationTag != null)    {        int flag = iterationTag.doAfterBody();        if(flag == BodyTag.EVAL_BODY_AGAIN)        {            tagEntry.setStatus(2);            stack.push(tagEntry);            return flag;        }        else        {            int startTagResult = tagEntry.getStartTagResult();            if(startTagResult != Tag.SKIP_BODY)            {                if(startTagResult != Tag.EVAL_BODY_INCLUDE)                {                    if(tag instanceof BodyTag)                    {                        pageContext.popBody();                    }                }            }        }    }    tag.release();    return tag.doEndTag();}

现在我们的模板引擎已经支持完整的jstl的tag功能了,而且我们避免了使用递归来渲染模板,性能还不错。
不过目前的实现仍然有不好的地方,编译有点慢,虽然我们可以使用缓存的机制确保它编译一次多次运行,在编译大文件的时候编译速度还是不太满意
而且程序结构有点复杂。
下一篇我们将重新设计一种类汇编的执行方式,编译速度将会有很大的提高,执行速度也将得到提升,同时代码结构也变得简单易读。

热点排行