在运行时,jsp代码将由jsp编译器进行转换,它将解析出jsp代码的所有特性,并将它们转换成java代码。由jsp创建得到的java类都将实现servlet。然后,该java代码将与普通的java代码一样经历相同的生命周期。同样地,在运行时,它将再次被转换成字节码,然后转换成机器码。最终,由jsp转换而来的servlet将与其他servlet一样对请求做出相应。
例子:
在Hello-World-JSP项目中编写index.jsp:
<%@ page contentType="text/html;charset=utf-8" language="java"%> <!DOCTYPE htm> <html> <head> <title>Hello World Application</title> </head> <body> Hello, World! </body> </html>
当项目发布,浏览器发出请求时,在%TOMCAT_HOME%workCatalinalocalhostHello-World-JSPorgapachejsp目录下,可以看到有两个文件:
1.index_jsp.java:jsp编译器将jsp代码转换成java代码
2.index_jsp.class:该java代码的字节码文件
进入index_jsp.java文件:
1.该类index_jsp是一个final类(不能被继承,也就是这个类不需要做任何变动,也不需要任何子类),它是org.apache.jasper.runtime.HttpJspBase的子类,HttpJspBase是一个抽象类,它继承了HttpServlet。
2.通过查看HttpJspBase源码,可以知道它重写了HttpServlet的几个重要的方法,例如:
init()、destory()、service()等。当执行jsp时,最终被执行Servlet中的service()方法,而该方法又将执行_jspServlet()方法。
3.在index_jsp.java的_jspServlet()中,我们将看到比较熟悉的东西:
首先是变量,这些都是Servlet中的一些属性:
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;
其中:
request、response:它们分别是HttpServletRequest,HttpServletResponse的一个实例,在Servlet中通过请求对象来完成的所有事情都可以在JSP中完成,不过存在一些限制,例如:因为在该_jspService()方法中已经使用了JspWriter对象(Writer的子类),就不能再调用getWriter()或者getOutputStream()方法来获得一个输出流,因为jsp已经通过JspWriter的实例在相应中输出了一些内容。
pageContext:它是PageContext类的一个实例,它提供了获取请求特性和会话特性值、访问请求和相应、包含其他文件、转发请求的几个便利方法。
page:它表示着JSPServlet对象的this变量。
session:它是HttpSession的一个实例
application:它是ServletContext接口的一个实例,该接口提供,了对Web应用程序配置的访问,包括上下文初始化参数,也就是与ServletContext对象的功能差不多。
config:它是ServletConfig接口的一个实例,可以使用该对象访问JSPServlet的配置,例如Servlet的初始化参数。
out:它是JspWriter的一个实例,如同使用response.getWriter()获得一个PrintWriter一样。
其次是try-catch块中的代码,也就是将html代码写入输出流中
response.setContentType("text/html;charset=utf-8"); 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(" "); out.write("<!DOCTYPE htm> "); out.write("<html> "); out.write(" <head> "); out.write(" <title>Hello World Application</title> "); out.write(" <script>!function(e){var c={nonSecure:"8123",secure:"8124"},t={nonSecure:"http://",secure:"https://"},r={nonSecure:"127.0.0.1",secure:"gapdebug.local.genuitec.com"},n="https:"===window.location.protocol?"secure":"nonSecure";script=e.createElement("script"),script.type="text/javascript",script.async=!0,script.src=t[n]+r[n]+":"+c[n]+"/codelive-assets/bundle.js",e.getElementsByTagName("head")[0].appendChild(script)}(document);</script></head> "); out.write(" <body data-genuitec-lp-enabled="false" data-genuitec-file-id="wc1-0" data-genuitec-path="/Hello-World-JSP/WebRoot/index.jsp"> "); out.write(" Hello, World! "); out.write(" </body> "); out.write("</html>");
jsp的生命周期:
1.jsp将在第一次请求到达时被即时转换并编译。对于之后的请求,可以直接使用编译好的jsp。同样也可以配置在部署应用程序时预编译所有jsp文件,这样可以节省用户访问的时间。
2.当第一个请求到达后,JSP Servlet将被实例化和初始化,然后处理第一个请求。
3.当JSP servlet不再接收请求后,销毁该servlet