当访问一个JSP页面时,内部是如何工作的
在运行时,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%\work\Catalina\localhost\Hello-World-JSP\org\apache\jsp目录下,可以看到有两个文件:
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("\r\n"); out.write("<!DOCTYPE htm>\r\n"); out.write("<html>\r\n"); out.write("\t<head>\r\n"); out.write("\t\t<title>Hello World Application</title>\r\n"); out.write("\t<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>\r\n"); out.write("\t<body data-genuitec-lp-enabled=\"false\" data-genuitec-file-id=\"wc1-0\" data-genuitec-path=\"/Hello-World-JSP/WebRoot/index.jsp\">\r\n"); out.write("\t\tHello, World!\r\n"); out.write("\t</body>\r\n"); out.write("</html>");
jsp的生命周期:
1.jsp将在第一次请求到达时被即时转换并编译。对于之后的请求,可以直接使用编译好的jsp。同样也可以配置在部署应用程序时预编译所有jsp文件,这样可以节省用户访问的时间。
2.当第一个请求到达后,JSP Servlet将被实例化和初始化,然后处理第一个请求。
3.当JSP servlet不再接收请求后,销毁该servlet