1.5 JSP页面结构和技术原理
在介绍完HTML和各种动态技术后,本节将让大家对JSP页面结构及其技术原理有个大概的了解。
1.5.1 一个简单的JSP页面
首先我们来看看一个简单的JSP页面,大家知道在学任何语言的第一个程序估计都是“Hello World!”程序,本小节将给大家展示JSP中的“Hello World!”程序是什么样的。其源代码如程序1-22所示。
【程序1-22】 HelloWorld.jsp
01 <%@ page language="java" import="java.util.*" 02 pageEncoding="ISO-8859-1"%> 03 <html> 04 <head> 05 <title> 06 HelloWorld! 07 </title> 08 </head> 09 <body> 10 <% 11 out.println("<h1>Hello World!</h1>"); 12 %> 13 </body> 14 </html>
程序1-22的结构和HTML文件很相似,只是其中多了一些特有的符号。其中第1和第2行是JSP的页面指令,该指令指出了JSP使用的脚本语言,还导入了相应的工具包。第3行到9行与HTML中的意义相同。
与 HTML 所不同的是第 10 行到 12 行,这是 JSP 中的脚本小程序,其功能是在浏览器上输出“<h1>Hello World!</h1>”的字样。不过这跟在浏览器上直接写“<h1>Hello World!</h1>”是不一样的,直接写是固定好了的,属于静态页面,而这里是动态的,也就是在此可以根据不同的请求,打印不同的语句。这在后面的章节中给出解释。将该文件放到 Web 服务器的相应位置,并进行相应的配置(具体环境的搭建和配置细节见第2章)。在浏览器中输入该文件的地址,出现如图1-13所示的结果。
图1-13 HelloWorld.jsp运行结果
我们还可以查看HTML的源代码,在网页上单击鼠标右键,在弹出的快捷菜单中选择“查看源文件”命令,得到如图1-14所示的源代码。
图1-14 HelloWorld.jsp运行后在浏览器中的源代码
从图1-14中我们可以看出,程序1-22中的“out.println("<h1>Hello World!</h1>");”,这条语句运行后,在浏览器上变成了“<h1>Hello World!</h1>”来显示结果。
1.5.2 一个典型的JSP页面文件
在上一节,我们给出了一个最简单的JSP页面,了解了它和HTML静态页面的区别,本小节我们将给出一个典型的JSP页面文件,通过该文件我们能对JSP有个大概的了解。在此我们可能看不懂代码是什么意思,不要紧,在学完了后面的章节后,我们将都会明白。程序 1-23 仅列出主要代码片段,完整程序请参照本书所附光盘。此代码为第8章中将要讲解的一个例子,为其中登录成功后的页面。
【程序1-23】loginSuccess.jsp
01 <%@ page language="java" \注:Page指令 02 import="java.util.*,com.bsw.book.*,com.bsw.user.*"pageEncoding="UTF-8" 03 %> 04 05 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 06 <html> 07 <head> 08 <title>My JSP 'bookList.jsp' starting page</title> 09 … 10 </head> 11 <body> 12 <jsp:useBean id="user" class="com.bsw.user.User"></jsp:useBean> 13 14 <%!String userName;%> 15 \注:声明 16 <%-- 设置响应页面的编码和文档类型--%> \注:注释 17 <% 18 request.setcharacterEncoding("UTF-8"); 19 response.setcharacterEncoding("UTF-8"); 20 response.setcontentType("text/html;charset=UTF-8"); 21 user = (User) session.getAttribute("user"); \注:脚本小程序 22 23 userName = user.getName(); 24 %> 25 <jsp:useBean id="book" class="com.bsw.book.Book" 26 scope="page"></jsp:useBean> 27 <table align="center" width="742" border="0"> 28 … 29 <tr> 30 <td> 31 <span class="style1"> 尊敬的<%=userName%> 32 ,欢迎您的到来!!</span> 33 </td> 34 … 35 </tr> 36 </table> 37 <form name="addShoppingCart" action="" method="post"> 38 … 39 <table border="0" width="740" align="center"> 40 <tr> 41 <td> 42 <div align="center" class="style1"> 43 图书名称 44 </div> 45 </td> 46 … 47 </tr> 48 … 49 <tr align="center" class="style1"> 50 <td> 51 <%=book.getName()%> 52 </td> 53 … 54 </tr> 55 <% 56 } 57 %> 58 </table> 59 </form> 60 </body> 61 </html>
我们可以看出,这个程序比简单的JSP页面复杂得多,在这个页面中包含了JSP的大部分成分,粗体的部分都是JSP的相关用法,具体细节在后面的章节进行详细介绍。其他的是HTML语法中元素的用法。
下面我们来看看它的运行结果,既然它比简单的JSP页面复杂那么多,那么它能实现的功能也是很丰富的。运行结果如图1-15所示。
图1-15 登录成功界面
1.5.3 JSP页面构成分析
从上一节的典型JSP页面可以看出,JSP包含了很多元素,如指令元素、注释、脚本元素、动作元素等。为了清晰地了解JSP页面的基本结构,图1-16按类别给出了JSP页面的组织结构图。
图1-16 JSP页面组织结构图
我们结合上一节的典型JSP页面的例子,来给出JSP页面的各部分组成。在程序1-23中,1、2行为page指令的用法。12行为动作指令的用法,动作指令有多种,如jsp:useBean,jsp:setProterty等。14行为声明的用法。16行为注释,该注释类型在客户端不显示。17~24行为脚本小程序的用法。31、51行等都为JSP表达式的用法。
1.5.4 JSP页面的执行过程
我们看到了典型的JSP页面的运行结果,那么到底JSP页面是怎样执行的呢?换句话说,JSP页面的生命周期是怎样的呢?
当我们通过Web浏览器向JSP服务器发出请求时,JSP服务器会检查是否存在该JSP页面对应的Servlet,如果存在,则会进一步检查该JSP页面有没有被更新,如果被更新过了,则将JSP翻译成Servlet源代码(即Java代码),然后对Java代码进行编译,使之变为class文件。然后将class文件加载到内存,并调用jspInit()函数对JSP进行初始化,紧接着调用_jspService()方法服务,然后将结果返回给客户端。如果第二次访问同一个JSP页面时,在服务器容器中已经存在了该JSP页面的Servlet,并没有被更新,则不用再重新转化为 Servlet,也不用重新编译,会直接调用_jspService()方法进行服务。其流程图如图1-17所示。
图1-17 JSP执行过程图
从图中可以看出,JSP文件在第一个用户访问该JSP页面时对JSP页面进行翻译,然后进行编译,翻译和编译比较花费时间。而第一个用户通常是该JSP页面的开发人员,这样用户访问该JSP页面时通常已经被转化成Servlet并已经被编译好了,访问的效率非常高。
1.5.5 翻译后的Java文件
在上一节中讲到,JSP页面要被翻译成Servlet文件,也就是Java文件。这个文件是经过JSP引擎翻译的。翻译好后通常放在服务器安装目录的work的相应目录下。如上面简单的JSP页面经翻译后的Java文件放在:安装目录\work\ Catalina\localhost\ch01\org\apache\jsp下。名字为HelloWorld_jsp.java。代码片段如程序1-24所示,完整程序请参照本书所附光盘。
【程序1-24】HelloWorld_jsp.java
01 package org.apache.jsp; 02 03 import javax.servlet.*; 04 import javax.servlet.http.*; 05 import javax.servlet.jsp.*; 06 import java.util.*; 07 08 public final class HelloWorld_jsp extends org.apache.jasper.runtime.HttpJspBase 09 implements org.apache.jasper.runtime.JspSourceDependent { 10 private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory(); 11 12 private static java.util.List _jspx_dependants; 得到工厂 13 14 private javax.el.ExpressionFactory _el_expressionfactory; 15 private org.apache.AnnotationProcessor _jsp_annotationprocessor; 16 17 public Object getDependants() { 18 return _jspx_dependants; 19 } 20 21 public void _jspInit() { 22 … 23 } 24 25 public void _jspDestroy() {} 26 27 public void _jspService(HttpServletRequest request, HttpServletResponse response) 28 throws java.io.IOException, ServletException { 29 PageContext pageContext = null; 30 HttpSession session = null; 31 ServletContext application = null; 32 ServletConfig config = null; 33 JspWriter out = null; 34 Object page = this; 35 JspWriter _jspx_out = null; 36 PageContext _jspx_page_context = null; 37 38 try { 39 response.setContentType("text/html;charset=ISO-8859-1"); 40 pageContext = _jspxFactory.getPageContext(this, request, response, 41 null, true, 8192, true); 42 _jspx_page_context = pageContext; 43 application = pageContext.getServletContext(); 44 config = pageContext.getServletConfig(); 45 session = pageContext.getSession(); 46 out = pageContext.getOut(); 47 _jspx_out = out; 48 49 out.write("\r\n"); 50 out.write(" 51 <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 52 Transitional//EN\">\r\n"); 53 out.write("<html>\r\n"); 54 out.write(" <head>\r\n"); 55 out.write(" <title>HelloWorld!</title>\r\n"); 56 out.write(" </head>\r\n"); 57 out.write(" \r\n"); 58 out.write(" <body>\r\n"); \注:向浏览器写内容 59 out.write(" "); 60 61 out.println("<h1>Hello World!</h1>"); 62 out.write("\r\n"); 63 out.write(" </body>\r\n"); 64 out.write("</html>"); 65 } 66 catch (Throwable t) { 67 if (!(t instanceof SkipPageException)) 68 { 69 out = _jspx_out; 70 71 if (out != null && out.getBufferSize() != 0) 72 try { out.clearBuffer(); } catch (java.io.IOException e) {} 73 74 if (_jspx_page_context != null) 75 _jspx_page_context.handlePageException(t); 76 } 77 } 78 79 finally { 80 _jspxFactory.releasePageContext(_jspx_page_context); 81 } 82 } 83 }
从该程序中我们可以看出,HelloWorld.jsp在运行时首先被解析成一个Java类:HelloWorld_jsp.java。该类继承于org.apache.jasper.runtime.HttpJspBase,实现了org.apache. jasper.runtime.JspSourceDependent。在_jspInit()中对变量_el_expressionfactory 和_jsp_annotationprocessor 进行了初始化。而_jspService 函数有两个参数HttpServletRequest类的对象request和HttpServletResponse类的对象response。它们分别代表了请求和响应。通过request可以得到请求的一些信息;通过response可以给浏览器响应。
在_jspService里,服务器对JSP文件进行解析之前首先对几个内置对象进行初始化。然后设置响应的格式和编码。接着通过 pageContext 得到各内置对象。而 pageContext 是通过_jspxFactory 的getPageContext()方法得到的。_jspxFactory是类JspFactory的对象。JspFactory是javax.servlet.jsp包中定义的一个抽象类,其中定义了两个静态方法 set/getDefaultFactory()。set 方法由 JSP 容器实例化该页面Servlet(即HelloWorld_jsp类)的时候置入,所以可以直接调用JspFactory.getDefaultFactory()方法得到这个JSP工厂的实现类。接着通过out对象向浏览器写入JSP页面中的各HTML标签和内容。当服务器容器不需要HelloWorld_jsp类的对象(可能是服务器关闭)时,就会调用_jspDestroy()进行清理工作。
通过这一节的讲解,我们掌握了 JSP 页面的构成,了解了 JSP 的实现原理和运行机制,这对 JSP编程是非常重要的。