技术库 > Java

JSP运行原理

技术库:tec.5lulu.com

JSP本质上就是把Java代码嵌套到HTML中,然后经过JSP容器(TomcatResinWeblogic等)的编译执行,再根据这些动态代码的运行结果生成对应的HTML代码,从而可以在客户端的浏览器中正常显示。

1 运行原理

如果JSP页面是第一次被请求运行,服务器的JSP编译器会生成JSP页面对应的JAVA代码,并且编译成类文件。当服务器再次收到对这个JSP页面请求的时候,会判断这个JSP页面是否被修改过,如果被修改过就会重新生成Java代码并且重新编译,而且服务器中的垃圾回收方法会把没用的类文件删除。如果没有修改过,服务器就会直接调用以前已经编译过的类文件。

举个例子,代码如下:

    <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>  
    <html>  
        <head>  
            <title>简单JSP页面示例</title>  
        </head>  
    <body>  
        这是一个简单的 JSP页面示例 <br>  
        </body>  
    </html>   

from:tec.5lulu.com


上面这个JSP页面在被请求的时候,Web服务器中JSP编译器会生成对应的Java文件,上面这个JSP程序在服务器中生成的对应Java代码如下:

    /* 
     * Generated by the Jasper component of Apache Tomcat  
     * Note: The last modified time of this file was set to 
     *       the last modified time of the source file after 
     *       generation to assist with modification tracking. 
     */  
    package org.apache.jsp;  
      
    import javax.servlet.*;  
    import javax.servlet.http.*;  
    import javax.servlet.jsp.*;  
    import java.util.*;  
      
    public final class Simple_jsp extends org.apache.jasper.runtime.HttpJspBase  
        implements org.apache.jasper.runtime.JspSourceDependent {  
      
      private static final javax.servlet.jsp.JspFactory _jspxFactory =  
              javax.servlet.jsp.JspFactory.getDefaultFactory();  
      
      private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;  
      
      private javax.el.ExpressionFactory _el_expressionfactory;  
      private org.apache.tomcat.InstanceManager _jsp_instancemanager;  
      
      public java.util.Map<java.lang.String,java.lang.Long> getDependants() {  
        return _jspx_dependants;  
      }  
      
      public void _jspInit() {  
        _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();  
        _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());  
      }  
      
      public void _jspDestroy() {  
      }  
      
      public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)  
            throws java.io.IOException, javax.servlet.ServletException {  
      
        final javax.servlet.jsp.PageContext pageContext;  
        javax.servlet.http.HttpSession session = null;  
        final javax.servlet.ServletContext application;  
        final javax.servlet.ServletConfig config;  
        javax.servlet.jsp.JspWriter out = null;  
        final java.lang.Object page = this;  
        javax.servlet.jsp.JspWriter _jspx_out = null;  
        javax.servlet.jsp.PageContext _jspx_page_context = null;  
      
      
        try {  
          response.setContentType("text/html;charset=gb2312");  
          pageContext = _jspxFactory.getPageContext(this, request, response,  
                    null, true, 8192, true);  
          _jspx_page_context = pageContext;  
          application = pageContext.getServletContext();  
          config = pageContext.getServletConfig();  
          session = pageContext.getSession();  
          out = pageContext.getOut();  
          _jspx_out = out;  
      
          out.write("rn");  
          out.write("<html>rn");  
          out.write("  <head>rn");  
          out.write("    <title>简单JSP页面示例</title>rn");  
          out.write(" </head>rn");  
          out.write("<body>rn");  
          out.write("    这是一个简单的 JSP页面示例 <br>rn");  
          out.write("  </body>rn");  
          out.write("</html>rn");  
        } catch (java.lang.Throwable t) {  
          if (!(t instanceof javax.servlet.jsp.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);  
        }  
      }  
    }   


上面这段代码就是刚才那个JSP所对应的Java代码,如果你用的服务器为Tomcat那么这个Java代码所在的位置为Tomcat(我的版本为7.0)目录下的workCatalinalocalhost你的项目名字orgapachejsp中。在这个目录下应该有两个对应的文件,一个是class文件一个是java文件。

上面这段程序本质就是一个servelet,它把所有页面的显示内容都用out对象打印出来,包括每个HTML标签,所以说JSP页面本质上就是Servelet的一种化身,在JSP程序中离不开Servelet的影子。这段代码的具体语法可以不必深究,这些工作都是由服务器中的JSP编译器来完成,这个过程是自动完成的,无需手动干预。

需要注意的是只有被请求过的页面才能生成对应的Java文件,没有请求的页面会在第一次请求的时候生成Java文件,当JSP页面被修改后,再次对这个页面进行请求才会重新生成对应的Java文件。

JSP的优势

JSP就是在HTML中嵌入Java代码,所以在本质上JSP程序就是Java程序,JSP程序继承了Java的一切优点。JSP程序有严格的Java语法和丰富的Java类库支持。

JSP页面在服务器中都会被JSP编译器编译成对应的Servlet,所以就拥有Java跨平台的优点,所有的JSP程序,无需改动就可以方便的迁移到其他操作系统平台,这就是在其他动态脚本中所无法想象的。

JSP中可以使用JavaBean进行逻辑封装,这样就可以实现逻辑功能代码的重用,从而大大提高系统的可重用性,同时也提高了程序的开发效率。

JSP程序容易上手,如果有HTMLJava的基本知识,那么学习JSP程序就没有任何难度。

JSP中可以使用Java众多的开源工具也是其他的动态网页语言无法比拟的。

由于以上种种优势JSP在众多的动态语言中成为开发人员最喜欢的语言之一。

2 JSP机制概述

可以把执行JSP页面的执行分成两个阶段,一个是转译阶段,一个是请求阶段。
转译阶段:JSP页面转换成Servlet类。
请求阶段:Servlet类执行,将响应结果发送至客户端。

1.用户(客户机)访问响应的JSP页面,如http://localhost:8080/Prj_test/ch02/HelloJSP.jsp
2.服务器找到相应的JSP页面。
3.服务器将JSP转译成Servlet的源代码。
4.服务器将Servlet源代码编译为class文件。
5.服务器将class文件加载到内存并执行。
6.服务器讲class文件执行后生成HTML代码发送给客户机,客户机浏览器根据响应的HTML代码进行显示。
如果该JSP页面为第一次执行,那么会经过这两个阶段,而如果不是第一次执行,那么将只会执行请求阶段。这也是为什么第二次执行JSP页面时明显比第一次执行要要快的原因。

如果修改了JSP页面,那么服务器将发现到该修改,并重新执行转译阶段和请求阶段。这也是为什么修改页面后访问速度变慢的原因。

jsp执行过程流程图

JSP运行原理,by 5lulu.com


1、jsp引擎
JSP 引擎实际上要把JSP标签、JSP页中的Java代码甚至连同静态HTML内容都转换为大块的Java代码。这些代码块被JSP引擎组织到用户看不到的 Java servlet中去,然后servlet自动把JVM(java虚拟机)编译成Java字节码。这样,当网站的访问者请求一个JSP页时,在他不知道的情 况下,一个已经生成的、预编译过的servlet实际上将完成所有的工作。非常隐蔽-而又高效。因为servlet是编译过的,所以网页中的JSP代码不 需要在每次请求该页时被解释一遍。JSP引擎只需在servlet代码最后被修改后编译一次,然后这个编译过的servlet就可以被执行了。由于是 JSP引擎自动生成并编译servlet,不用程序员动手编译代码,所以JSP能带给你高效的性能和快速开发所需的灵活性。
2、web容器和servlet容器
servlet 容器的主要任务是管理servlet的生命周期。web容器更准确的说应该叫web服务器,它是来管理和部署web应用的。还有一种服务器叫做应用服务 器,它的功能比web服务器要强大的多,因为它可以部署EJB应用,可以实现容器管理的事务,一般的应用服务器有weblogic和websphere 等,它们都是商业服务器,功能强大但都是收费的。web容器最典型的就是tomcat了,Tomcat是web容器也是servlet容器

2、jsp工作原理
当 web容器(tomcat、jboss等等)接收到用户的第一个jsp页面请求时,jsp引擎将这个jsp页面转换为java源代码(servlet 类),在转换过程中,如果发现jsp文件有任何的语法错误,转换过程将终止,并向服务器和客户端输出错误信息,如果转换成功,然后jsp引擎用javac 编译java源代码生成class文件,然后web容器加载class文件并从此创建一个新的servlet对象进行实例化,当 Servlet 类实例化后,容器加载 jsinit,以通知 servlet 它已进入服务行列。init 方法必须被加载,Servelt 才能接收和请求。假如要载入数据库驱动程序、初始化一些值等等,程序员可以重写这个方法。在其他情况下,这个方法一般为空,jspInit()方法在 servlet的生命周期中只被执行一次。然后jspService()方法被调用来处理客户端的请求。容器创建一个响应文档,次文档发送给用户,如干时 间后,用户再次访问这个j请求这个jsp时,容器回再次创建响应一个文档,直到容器卸载了这个class文件,当用户卸载了这个class文件后,再次访 问时,jsp引擎并不重新转换和编译这个jsp文件,而是对它进行重新初始化,并创建一个响应文档,返回给客户端。对每一个请求,web容器创建一个新的 线程来处理该请求。如果有多个客户端同时请求该JSP文件,则JSP引擎会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统 的资源需求,提高系统的并发量及响应时间.但应该注意多线程的编程限制,由于该servlet始终驻于内存,所以响应是非常快的。如果.jsp文件被修改 了,服务器将根据设置决定是否对该文件重新编译,如果需要重新编译,则将编译结果取代内存中的servlet,并继续上述处理过程。如果在任何时候如果由 于系统资源不足的原因,web容器将以某种不确定的方式将servlet从内存中移去。当这种情况发生时jspDestroy()方法首先被调用, 然后servlet实例便被标记加入"垃圾收集"处理。


3、jsp脚本与声明的差异
  1. <%!int count=100;%>--------jsp声明   
  2. <%int count=100;%>---------jsp脚本  
<%!int count=100;%>--------jsp声明
<%int count=100;%>---------jsp脚本  
二者的差异在于作用域和生存期,
(1)、jsp声明中创建的名字有类范围的作用域和生存期
(2)、jsp脚本中创建的名字有局限于方法的作用域和生存期。
二者的作用域就像是java中在类中定义一个属性A和在类的方法中定义一个属性B,
类中不能引用属性B,但是在方法中可以引用属性A,
二者的生存期:
jsp声明,例如:<%!int count=100;%><%=count++%>
脚本中的变量生存期存在于第一个用户延续到第二个用户。。。。,如果第一个用户第一次访问时100,第二个用户访问就101,第三个用户访问时102,以此类推。。。如果服务器停止而重新启动后,则count值就返回到100,
jsp脚本,例如:<%int count=100;%><%=count++%>
脚本中的变量生存期存在于每个用户的访问期间,所以没有用户访问都是100
无论声明和脚本放置的位置不同,jsp容器都是首先进行初始化声明,再执行脚本的。

3 总结

(1)、不能在脚本中定义方法,但可以再jsp声明中定义自己的方法,因为脚本程序是局限于jspService方法中的,如果在jspservice方法中再次定义方法是不允许的。
(2)、不能在jso声明中使用out等隐藏对象,因为out等隐藏对象,是作用域jspservice方法中定义的。
(3)、脚本中定义变量,不能在jsp声明中引用此变量。
(4)、如果变量定义在方法中,则不能在方法之前使用此变量。

JSP运行原理


本文链接 http://tec.5lulu.com/detail/110dcn2eh9gjw85e0.html

我来评分 :6.1
0

转载注明:转自5lulu技术库

本站遵循:署名-非商业性使用-禁止演绎 3.0 共享协议

www.5lulu.com