`
JAVA海洋
  • 浏览: 618002 次
  • 性别: Icon_minigender_1
  • 来自: 太原
社区版块
存档分类
最新评论

JSP的运行内幕

阅读更多
经常有朋友问起,JSP和Servlet之间有什么区别,两者之间又有什么联系?其实Servlet技术的出现时间很早,是当时为了Java的服务器端应用而开发的。大家都知道Applet是应用小程序,Servlet就是服务器端小程序了。但在Microsoft公司的ASP技术出现后,使用Servlet进行响应输出时一行行的输出语句就显得非常笨拙,对于复杂布局或者显示页面更是如此。JSP就是为了满足这种需求在Servlet技术之上开发的。可见,JSP和Servlet之间有着内在的血缘关系,在学习JSP时,如果能够抓住这种联系,就能更深刻地理解JSP的运行机理,达到事半功倍的效果。

本文将通过对一个JSP运行过程的剖析,深入JSP运行的内幕,并从全新的视角阐述一些JSP中的技术要点。

HelloWorld.jsp

我们以Tomcat 4.1.17服务器为例,来看看最简单的HelloWorld.jsp是怎么运行的。

代码清单1:HelloWorld.jsp

<ccid_nobr></ccid_nobr>
<ccid_code><font size="4"><font color="#a52a2a">HelloWorld.jsp
&lt;%
 String message = "Hello World!";
%&gt;
&lt;%=message%&gt;</font></font></ccid_code>


  这个文件非常简单,仅仅定义了一个String的变量,并且输出。把这个文件放到Tomcat的webapps\ROOT\目录下,启动Tomcat,在浏览器中访问http://localhost:8080/HelloWorld.jsp,浏览器中的输出为“HelloWorld!”

  让我们来看看Tomcat都做了什么。转到Tomcat的\work\Standalone\localhost\_目录下,可以找到如下的HelloWorld_jsp.java,这个文件就是Tomcat解析HelloWorld.jsp时生成的源文件:

  代码清单2:HelloWorld_jsp.java

<ccid_nobr></ccid_nobr>
<ccid_code><font size="4"><font color="#a52a2a">package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;

public class HelloWorld_jsp extends HttpJspBase {
 ......
public void _jspService(HttpServletRequest request, 
HttpServletResponse response)throws java.io.IOException, ServletException
 {
  JspFactory _jspxFactory = null;
  javax.servlet.jsp.PageContext pageContext = null;
  HttpSession session = null;
  ServletContext application = null;
  ServletConfig config = null;
  JspWriter out = null;
  Object page = this;
  JspWriter _jspx_out = null;

  try {
   _jspxFactory = JspFactory.getDefaultFactory();
   response.setContentType("text/html;charset=ISO-8859-1");
   pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
   application = pageContext.getServletContext();
   config = pageContext.getServletConfig();
   session = pageContext.getSession();
   out = pageContext.getOut();
   _jspx_out = out;

   String message = "Hello World!";
   out.print(message);
  } catch (Throwable t) {
   out = _jspx_out;
   if (out != null &amp;&amp; out.getBufferSize() != 0)
    out.clearBuffer();
   if (pageContext != null) pageContext.handlePageException(t);
  } finally {
  if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
  }
 }
}</font></font></ccid_code>


  从上面可以看出,HelloWorld.jsp在运行时首先解析成一个Java类HelloWorld_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 4.1.17)的一个JspFactory对象的引用。JspFactory是javax.servlet.jsp包中定义的一个抽象类,其中定义了两个静态方法set/getDefaultFactory()。set方法由JSP容器(Tomcat)实例化该页面Servlet(即HelloWorld_jsp类)的时候置入,所以可以直接调用JspFactory.getDefaultFactory()方法得到这个JSP工厂的实现类。Tomcat是调用org.apache.jasper.runtime.JspFactoryImpl类。

  然后,调用这个JspFactoryImpl的getPageContext()方法,填充一个PageContext返回,并赋给内置变量pageConext。其它内置对象都经由该pageContext得到。具体过程见上面的代码,这里不再赘述。该页面Servlet的环境设置完毕,开始对页面进行解析。HelloWorld.jsp页面仅仅定义了一个String变量,然后直接输出。解析后的代码如下:

  代码清单3:JSP页面解析后的代码片断

<ccid_nobr></ccid_nobr>
<ccid_code><font size="4"><font color="#a52a2a">String message = "Hello World!";
out.print(message);</font></font></ccid_code>


  定制标签的解析过程

  在一个中大型的Web应用中,通常使用JSP定制标签来封装页面显示逻辑。剖析容器对定制标签的解析过程,对我们深入理解定制标签的运行机理非常有帮助。下面我们以Struts1.1b中附带的struts-example应用的主页运行为例加以说明。

  包含定制标签的index.jsp

  Struts1.1b的下载地址是http://jakarta.apache.org/struts/index.html。将下载的包解压,在webapps目录下可以找到struts-example.war。将该War包拷贝到Tomcat的webapps目录下,Tomcat会自动安装此应用包。在浏览器中通过http://localhost:8080/struts-example访问struts-example应用,将显示应用的首页(见图1)。



  图一 应用的首页

  代码清单4:index.jsp

<ccid_nobr></ccid_nobr>
<ccid_code><font size="4"><font color="#a52a2a">&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %&gt;
&lt;html:html locale="true"&gt;
&lt;head&gt;
&lt;title&gt;&lt;bean:message key="index.title"/&gt;&lt;/title&gt;
&lt;html:base/&gt;
&lt;/head&gt;
&lt;body bgcolor="white"&gt;
…… 
&lt;/body&gt;
&lt;/html:html&gt;</font></font></ccid_code>


<ccid_code><font size="4"><font color="#a52a2a">  我们仅以index.jsp中的&lt;bean:message/&gt;标签的解析为例进行分析,看容器是怎样把这个自定义标签解析成HTML输出的。上面代码省略了页面的其它显示部分。首先,查看上面浏览器中页面的源文件:</font></font></ccid_code>

<ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code><font size="4"><font color="#a52a2a">&lt;html lang="zh"&gt; &lt;head&gt; &lt;title&gt;MailReader Demonstration Application (Struts 1.0)&lt;/title&gt; &lt;/head&gt; &lt;body bgcolor="white"&gt; …… &lt;/body&gt; &lt;/html&gt;</font></font></ccid_code></pre></td></tr></tbody></table></ccid_nobr>

<ccid_code><font size="4"><font color="#a52a2a">  可见,容器已经把&lt;bean:message key="index.title"/&gt;替换成一个字串,显示为页面的标题。</font></font></ccid_code>

  解析过程

  那么,JSP容器是怎样完成解析的呢?查看在工作目录jakarta-tomcat-4.1.17\work\Standalone\localhost\struts-example下解析后的index_jsp.java文件:

  代码清单5:index_jsp.java

<ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code><font size="4"><font color="#a52a2a">package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import org.apache.jasper.runtime.*; public class index_jsp extends HttpJspBase { //为所有的定制标签定义处理器池类的引用 private org.apache.jasper.runtime.TagHandlerPool ; _jspx_tagPool_bean_message_key; …… //页面类构造方法 public index_jsp() { _jspx_tagPool_bean_message_key = new org.apache.jasper.runtime.TagHandlerPool(); …… } public void _jspService(HttpServletRequest request,   HttpServletResponse response)   throws java.io.IOException, ServletException { …… _jspxFactory = JspFactory.getDefaultFactory(); response.setContentType("text/html;charset=UTF-8"); pageContext = _jspxFactory.getPageContext(this,   request, response,null, true, 8192, true); application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; …… if (_jspx_meth_html_html_0(pageContext)) return; …… } //页面在处理退出时释放所有定制标签的属性 public void _jspDestroy() { _jspx_tagPool_bean_message_key.release(); …… } }</font></font></ccid_code></pre></td></tr></tbody></table></ccid_nobr>

  生成的index_jsp.java继承于org.apache. jasper.runtime.HttpJspBase。研究这个文件为我们了解定制标签的运行机理提供了途径。

<ccid_code>  从上面可以看出,Tomcat在解析一个JSP页面时,首先为每一个定制标签定义并实例化了一个TagHandlerPool对象。页面的处理方法覆盖父类的_ jspService()方法,_jspService方法首先初始化环境,为内置对象赋值。由于index.jsp页面整体由一个&lt;html:html/&gt;标签包裹,Tomcat对每一个标签都产生一个私有方法加以实现。&lt;html:html/&gt;标签的处理方法是_jspx_meth_html_html_0()。这个方法的命名规范大家也可以从这里看出,就是“_jspx_meth + 标签的前缀 + 标签名 + 该标签在JSP页面同类标签中出现的序号”。其它标签都被包含在该标签中,所以其它标签在_jspx_meth_html_html_0()方法中进行解析。具体的代码实现请参见赛迪网http://linux.ccidnet.com期刊浏览2003年第6期。</ccid_code>

<ccid_code>  在_jspx_meth_html_html_0()方法中,首先从_jspx_tagPool_html_html_locale池中得到一个org.apache.struts.taglib.html.HtmlTag的实例,然后设置这个tag实例的页面上下文及上级标签,由于html:html标签是页面的最顶层标签,所以它的parent是null。然后对该标签的内容进行解析。HTML代码直接输出,下面主要看看&lt;html:html&gt;&lt;/html:html&gt;标签之间包含的&lt;bean:message key="index.title"/&gt;标签的解析。对bean:message标签的解析类似于html:html,Tomcat也将其放入一个单独的方法_jspx_meth_bean_message_0()中进行。</ccid_code>

  bean:message标签的解析

  代码清单7:_jspx_meth_bean_message_0()方法片断

<ccid_nobr></ccid_nobr>
<ccid_code><font size="4"><font color="#a52a2a">//对message定制标签的处理方法
private boolean _jspx_meth_bean_message_0(
javax.servlet.jsp.tagext.Tag _jspx_th_html_html_0, 
javax.servlet.jsp.PageContext pageContext) throws Throwable {
 JspWriter out = pageContext.getOut();
 /* ----  bean:message ---- */
 org.apache.struts.taglib.bean.MessageTag
 _jspx_th_bean_message_0 =
 (org.apache.struts.taglib.bean.MessageTag) 
 _jspx_tagPool_bean_message_key.get(
 org.apache.struts.taglib.bean.MessageTag.class);
 _jspx_th_bean_message_0.setPageContext(pageContext);
 _jspx_th_bean_message_0.setParent(_jspx_th_html_html_0);
 _jspx_th_bean_message_0.setKey("index.title");
 int _jspx_eval_bean_message_0 = _jspx_th_bean_message_0.doStartTag();
 if (_jspx_th_bean_message_0.doEndTag()== javax.servlet.jsp.tagext.Tag.SKIP_PAGE)
  return true;
 _jspx_tagPool_bean_message_key.reuse(_jspx_th_bean_message_0);
  return false;
}</font></font></ccid_code>


<ccid_code><font size="4"><font color="#a52a2a">  同样,对html:bean也需要从池中得到一个标签类的实例,然后设置环境。这里不再赘述。我们只专注对MessageTag定制标签类特殊的处理部分。定制标签类的开发不在本文讨论范围之内。在index.jsp中定义了一个bean:message标签,并设置了一个属性:&lt;bean:message key="index.title"/&gt;。Tomcat在解析时,调用MessageTag对象的key属性设置方法setKey(),将该属性置入。然后调用MessageTag的doStartTag()和doEndTag()方法,完成解析。如果doEndTag()方法的返回值为javax.servlet.jsp.tagext.Tag. SKIP_PAGE,表明已经完成解析,返回true,Tomcat将立即停止剩余页面代码的执行,并返回。否则把该MessageTag的实例放回池中。</font></font></ccid_code>

  标签类对象实例的池化

  为了提高运行效率,Tomcat对所有的定制标签类进行了池化,池化工作由org.apache.jasper. runtime.TagHandlerPool类完成。TagHandlerPool类主要有两个方法,代码如下:

  代码清单8:TagHandlerPool.java

<ccid_nobr><table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1"><tbody><tr><td class="code" bgcolor="#e6e6e6"><pre><ccid_code><font size="4"><font color="#a52a2a">public class TagHandlerPool { private static final int MAX_POOL_SIZE = 5; private Tag[] handlers; public synchronized Tag get(Class handlerClass) throws JspException {……} public synchronized void reuse(Tag handler) {……} }</font></font></ccid_code></pre></td></tr></tbody></table></ccid_nobr>

  TagHandlerPool简单地实现了对标签类的池化,其中MAX_POOL_SIZE是池的初始大小,handlers是一个Tag的数组,存储标签类的实例。get(Class handlerClass)得到一个指定标签类的实例,如果池中没有可用实例,则新实例化一个。reuse(Tag handler)把handler对象放回池中。

  至此,我们对JSP在容器中的运行过程已经了然于胸了。虽然每种JSP容器的解析结果会有差异,但其中的原理都雷同。对于编写JSP应用,我们并不需要干涉容器中的运行过程,但如果你对整个底层的运行机制比较熟悉,就能对JSP/Servlet技术有更深的认识。
分享到:
评论

相关推荐

    JSP运行内幕

    ### J2EE JSP运行内幕解析 #### 一、引言 JSP(Java Server Pages)作为Java技术栈中的一个重要组成部分,在Web应用开发领域扮演着举足轻重的角色。它结合了HTML标记与Java代码的强大功能,使得动态网页的创建变得...

    tomcat+jsp内幕详解

    **JSP运行原理** JSP(JavaServer Pages)是一种动态网页技术,它将HTML代码与Java代码混合编写。当用户请求一个JSP页面时,Tomcat会将其转换为一个Servlet,并编译成.class文件。这个过程由Jasper完成。然后,...

    Tomcat运行内幕-How Tomcat Works

    《Tomcat运行内幕-How Tomcat Works》这本书深入解析了Java Web服务器,特别是Apache Tomcat的工作机制,对于理解和优化Java EE应用的性能至关重要。虽然提供的章节有限,但我们仍能从中获取到许多关键知识点。 ...

    javaweb开发JSP资料大全

    JSP安全编程实例浅析、JSP编程进度条设计实例、JSP的运行内幕、JSP和IIS的最佳解决方案实例分析、jsp内置对象--session对象和out对象、JSP中request属性的用法、用WebWork、JSP、Velocity建立注册页面、在JSP中使用...

    howtomcatworks tomcat运行内幕

    《Tomcat运行内幕详解》 Apache Tomcat,作为一款开源且免费的Java Servlet容器,是许多Web应用程序开发者的首选。了解Tomcat的工作原理对于优化应用性能、排查问题以及提升运维效率至关重要。本篇文章将深入剖析...

    JSP程序设计从入门到精通

    #### JSP运行时错误处理 在JSP页面中可以添加错误处理逻辑,比如try-catch块来捕获可能发生的Java异常,并给出相应的处理策略。 #### JSP实例 通过具体的实例来加深对JSP语法和特性的理解,比如如何在JSP页面中...

    JSP 程序设计从入门到精通 PDF 教程

    - 这部分讨论了常见的JSP运行时错误以及如何处理这些问题,同时列举了一些开发过程中需要注意的事项。 ##### 2.8 JSP小实例 - **2.8.1 实例1(在JSP中定义函数)**:这部分提供了一个简单的示例,展示如何在JSP...

    深入体验Java Web开发内幕

    本书深刻且通俗地揭示Java Web开发内幕,使您由内而外地明白使用Java进行Web应用开发的全过程——从XML基础知识到HTTP详述及相关体验,从用Tomcat配置Web站点到HttpServletResponse和HttpServletRequest的应用,以及...

    深入分析JavaWeb技术内幕 修订版PDF

    JSP页面在服务器运行时转换为Servlet,然后由Web容器执行。JSP的使用简化了动态网页的开发,因为它允许开发者直接在HTML中嵌入Java代码片段。常见的JSP标签库包括JSTL(JavaServer Pages Standard Tag Library)。 ...

    Java Web 开发内幕.rar

    7. **Tomcat与Jetty**:这些都是流行的Java Web应用服务器,用于部署和运行Servlet和JSP应用。Tomcat是开源且轻量级的,而Jetty同样轻量且高效,广泛用于嵌入式场景。 8. **Web服务**:包括SOAP(Simple Object ...

    JavaWeb技术内幕(修订版).pdf

    容器负责管理Servlet和JSP的生命周期,处理请求和响应,提供运行环境。 7. **MVC架构模式**:Model-View-Controller模式是JavaWeb开发中常见的设计模式,用于分离业务逻辑、视图展示和用户交互。Model代表业务逻辑...

    深入分析Java Web技术内幕 高清完整中文版PDF下载

    由于给定的文件内容中除了标题、描述、标签以及部分关于Hadoop、大数据、AI的内容外,并没有提供具体的关于“Java Web技术内幕”的内容,所以我将无法提供深入分析Java Web技术内幕的详细知识点。但是,我可以根据所...

    《Spring技术内幕》

    依赖注入是Spring最核心的功能之一,通过控制反转(Inversion of Control, IoC)原理,降低程序的耦合度,使得对象间的依赖关系由Spring容器在运行期间动态地管理和注入。在Spring的依赖注入机制中,有两种注入方式...

    java web整合开发王者归来光盘代码-JSP章节

    **Servlet**是Java Web的基础,它是运行在服务器端的小程序,处理客户端请求并返回响应。Servlet生命周期包括加载、初始化、服务和销毁四个阶段。 **Struts**是MVC(Model-View-Controller)框架的早期代表,用于...

    Java Web技术内幕(修订版)

    《Java Web技术内幕(修订版)》是一本深入探讨Java Web开发的专业书籍,它由阿里巴巴集团的技术专家联合推荐,旨在分享大型互联网公司在开发应用过程中的实践经验。这本书详细讲解了Java Web开发中的各种核心技术,...

    Java_Web开发内幕-核心基础

    1. **Servlet与JSP**:Servlet是Java Web开发的基础,它是服务器端运行的Java程序,用于处理HTTP请求。JSP(Java Server Pages)则是一种动态网页技术,允许在HTML中嵌入Java代码,使得网页具有更强的动态性。书中...

    百度云下载--《深入分析Java Web技术内幕》修订版 许令波著 PDF扫描版[92M]

    Servlet是一种运行在服务器端的小程序,用于处理客户端的HTTP请求,并生成响应结果返回给客户端。Servlet是Java Web开发的基础之一,通过学习Servlet的工作原理、生命周期、配置方法等内容,可以更好地理解Java Web...

Global site tag (gtag.js) - Google Analytics