return to homepage.

three ways contact with meemail: contact with me using emailmsn: contact with me using msnQQ: contact with me using QQ

欢迎自由转载,但请注明本blog链接,谢谢合作。

文章分类

成长,转载 (1) 地税 (1) 健康 (2) 趣闻 (1) 生活杂记 (11) 转载 (4) CSS (1) Design Patterns (1) DWR (3) EJB (1) Hibernate (2) javascript (2) Jsp (2) NBA (1) Oracle (2) Struts (4) Tomcat (1) Weblogic (1) XML (1)

2007年3月6日星期二

JSP运行机制不完全详解

虽然不关心JSP运行时的很多细节也能做出项目来,不过,这就像进入到热水壶中的青蛙,无法预知自已很有可能死到临头,当然也许程序中没有那样严重。那下面我就来谈谈我对JSP运行机制的一些理解和记录自己的一些知识。

Web容器(Servlet引擎)接收到以.jsp为扩展名的url http访问请求后,实质是交给了一个JSP引擎支处理,这个引擎就是一个servlet,名叫org.apache.jasper.servlet.JspServlet。当每个JSP页面在第一次被访问的时候,JSP引擎就会把它翻译成一个servlet源程序,接着再把这个servlet源程序编译成一个servlet的class类文件,然后再由Web容器像普通servlet程序一样的方式来装载和解释执行。

看下面的一段代码
这是livahu.jsp源文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--
author: livahu--%>
<%
    
String time = new java.util.Date().toString();
%>
<html>
    <head>
    </head>
    <body>
        <h1>
current time is<%=time%></h1>
    </body>
</html>

我想大家都十分明白它是在做什么
但大家看看它的servlet源码吧

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class livahu_jsp extends org.apache.jasper.runtime.HttpJspBase
    
implements org.apache.jasper.runtime.JspSourceDependent {

  
private static final JspFactory _jspxFactory JspFactory.getDefaultFactory();

  
private static java.util.List _jspx_dependants;

  
private javax.el.ExpressionFactory _el_expressionfactory;
  
private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  
public Object getDependants() {
    
return _jspx_dependants;
  }

  
public void _jspInit() {
    
_el_expressionfactory _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    
_jsp_annotationprocessor = (org.apache.AnnotationProcessorgetServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  
public void _jspDestroy() {
  }

  
public void _jspService(HttpServletRequest requestHttpServletResponse response)
        
throws java.io.IOExceptionServletException {

    
PageContext pageContext null;
    
HttpSession session null;
    
ServletContext application null;
    
ServletConfig config null;
    
JspWriter out null;
    
Object page this;
    
JspWriter _jspx_out null;
    
PageContext _jspx_page_context null;


    
try {
      
response.setContentType("text/html;charset=UTF-8");
      
pageContext _jspxFactory.getPageContext(thisrequestresponse,
                  
nulltrue8192true);
      
_jspx_page_context pageContext;
      
application pageContext.getServletContext();
      
config pageContext.getServletConfig();
      
session pageContext.getSession();
      
out pageContext.getOut();
      
_jspx_out out;

      
out.write('\r');
      
out.write('\n');
      
out.write('\r');
      
out.write('\n');

    
String time new java.util.Date().toString();

      
out.write("\r\n");
      
out.write("<html>\r\n");
      
out.write("\t<head>\r\n");
      
out.write("\t</head>\r\n");
      
out.write("\t<body>\r\n");
      
out.write("\t\t<h1>current time is");
      
out.print(time);
      
out.write("</h1>\r\n");
      
out.write("\t</body>\r\n");
      
out.write("</html>");
    } 
catch (Throwable t) {
      
if (!(instanceof SkipPageException)){
        
out _jspx_out;
        
if (out != null && out.getBufferSize() != 0)
          
try out.clearBuffer(); } catch (java.io.IOException e) {}
        
if (_jspx_page_context != null_jspx_page_context.handlePageException(t);
      }
    } 
finally {
      
_jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}

从上面可以看出,livahu.jsp在运行时首先解析成一个Java类livahu_jsp.java,该类继承于org.apache.jasper.runtime.HttpJspBase基类,HttpJspBase实现了HttpServlet接口。可见,JSP在运行前首先将编译为一个Servlet,这就是理解JSP技术的关键。

JSP页面中内置了几个对象,如pageContext、application、config、page、session、out等,你想过没有,为什么在JSP中的脚本片断中可以直接使用这些内置对象。观察_jspService()方法,实际上这几个内置对象就是在这里定义的。在对JSP文件中的代码片断进行解析之前,先对这几个内置对象进行初始化。首先,调用JspFactory的getDefaultFactory()方法获取容器实现(本文中指Tomcat 6.0.10)的一个JspFactory对象的引用。JspFactory是javax.servlet.jsp包中定义的一个抽象类,其中定义了两个静态方法setDefaultFactory()和getDefaultFactory()。set方法由JSP容器(Tomcat)实例化该页面Servlet(即livahu_jsp类)的时候置入,所以可以直接调用JspFactory.getDefaultFactory()方法得到这个JSP工厂的实现类。Tomcat是调用org.apache.jasper.runtime.JspFactoryImpl类。 然后,调用这个JspFactoryImpl的getPageContext()方法,填充一个PageContext返回,并赋给内置变量pageConext。其它内置对象都经由该pageContext得到。具体过程见上面的代码,这里不再赘述。该页面Servlet的环境设置完毕,开始对页面进行解析。livahu.jsp页面仅仅定义了一个String变量,然后直接输出。

完了吗?当然没有,知道这些还不够,有没有想过,JSP页面中的指令元素、JSP标签(实际是JSP动作元素)、你自己的定制标签是怎么样被解析的呢?呵呵,本文的标题为什么是不完全详解呢,就在这里揭晓,我个人觉得有了上面的提示,读者也许会知道,想了解什么具体元素或标签的问题可以去看翻译后的servlet的Java源文件就行了,也许你比我发现的还多。但注是以下几点:

一、<jsp:include>是动态引入的页面内容,即在翻译的源文件中不会反它指的页面内容一起加入来;这与<%@ include%>正好相反,它是静态的,为什么?看源文件吧。(我比较懒的,但如有问题欢迎留言,我尽力可以帮你分析)
二、JSP页面中有三种注释的方法:JSP注释(<%-- hello everyone ! --%>)Java注释(//hello /* everyone !*/)、HTML注释(<!-- hello everyone ! -->)大家可以分析一下源码,看看它们有什么区别。看是不是这样:JSP引擎在翻译JSP页面时,将忽略JSP注释的内容;在servlet源文件中Java注释会在中间原样摆着JSP引擎认为是Java代码的一部份,但在编译时将不存在;HTML注释引擎看来是一段文本,注意JSP引擎对文本的处理方式:有标签就要翻译标签(包括非JSTL中的tag比如struts tag),有scriptlet就要翻译scriptlet。

0 评论: