【转载】http://www.bingfengsa.com/old/archives/9921.html


根据源码,我看到JSP编译的顺序是这样的:

 

1– getJspConfigPageEncoding

2– determineSyntaxAndEncoding

3– 解析成 Node.Nodes parsedPage 对象,即取出所有节点

4– 解析每个节点

 

1步,是从web.xml等配置文件中去读取配置(里面有个<jsp-config>配置),如果配置时设置了统一编码,则使用这种类型的编码,第2步,是根据文件来获取编码,注意看如下一段代码:

      if ((jspReader.matches(“tag ”)) || (jspReader.matches(“page”)))

      {

        jspReader.skipSpaces();

        Attributes attrs = Parser.parseAttributes(this, jspReader);

        encoding = getPageEncodingFromDirective(attrs, ”pageEncoding”);

        if (encoding != null) {

          break;

        }

        encoding = getPageEncodingFromDirective(attrs, ”contentType”);

        if (encoding != null) {

          saveEncoding = encoding;

        }

      }

    }

程序首先判断有无编译指令tag或者page,如果有,则检查编译指令是否指定了pageEncoding属性或者contentType属性。根据这种逻辑,可知如下这种写法:

<%@ page contentType=”text/html;charset=utf-8″ pageEncoding=”UTF-8″%>

其实是重复指定了编码,解析时会以pageEncoding为准

 

4步,解析每个节点:

while (reader.hasMoreInput()) {

        parser.parseElements(root);

      }

这里又分为几个步骤,先看程序:

  private void parseElements(Node parent)

    throws JasperException

  {

    this.start = this.reader.mark();

    if (this.reader.matches(“<%–”)) {

      parseComment(parent);

    } else if (this.reader.matches(“<%@”)) {

      parseDirective(parent);

    } else if (this.reader.matches(“<jsp:directive.”)) {

      parseXMLDirective(parent);

    } else if (this.reader.matches(“<%!”)) {

      parseDeclaration(parent);

    } else if (this.reader.matches(“<jsp:declaration”)) {

      parseXMLDeclaration(parent);

    } else if (this.reader.matches(“<%=”)) {

      parseExpression(parent);

    } else if (this.reader.matches(“<jsp:expression”)) {

      parseXMLExpression(parent);

    } else if (this.reader.matches(“<%”)) {

      parseScriptlet(parent);

    } else if (this.reader.matches(“<jsp:scriptlet”)) {

      parseXMLScriptlet(parent);

    } else if (this.reader.matches(“<jsp:text”)) {

      parseXMLTemplateText(parent);

    } else if ((!(this.pageInfo.isELIgnored())) && (this.reader.matches(“${“))) {

      parseELExpression(parent, ’$');

    } else if ((!(this.pageInfo.isELIgnored())) && (this.reader.matches(“#{“)))

    {

      parseELExpression(parent, ’#');

    } else if (this.reader.matches(“<jsp:”)) {

      parseStandardAction(parent);

    } else if (!(parseCustomTag(parent))) {

      checkUnbalancedEndTag();

      parseTemplateText(parent);

    }

  }

 

处理的顺序如下:

1– <%– –%>”类型的注释

2– <%@ %>”编译指令

3– <jsp:directive. %>”编译指令

4– <%! %>”声明指令

5– <%= %>”表达式指令

6– <% %>”嵌入脚本

7– <jsp:text >”嵌入文本

8– ${ }EL表达式

9– #{ }EL表达式

10– <jsp: >”其他jsp动作指令

11– 自定义的tag标签

 

然后,再看看jsp中的java代码(ScriptingElement)是怎么执行的:

 

第一步:

new一个Node节点,然后把java的字符串完整地赋值给Nodetext属性,然后把node添加到Parent Node 队列(List)里面

 

第二步:读取这些Nodes,将其转换成java源代码,然后在调用java编译器将源代码编译成class文件。(注意:这个功能相当于是把字符串,转换成了java字节码)

这个过程,调用了SmapUtil将上面那些nodes转换成Java源文件,然后调用JDTCompiler工具类,将Java源文件编译成.class文件,我看Tomcat调用的是org.eclipse.jdt.internal.compiler.*包下面的编译工具,实际上JDK也为我们提供了自己手动编译Java文件的方法,JDK 1.6可以用javax.tools.JavaCompiler