分析JSP所生成的Servlet代码
当用户第一次访问JSP页面时,该页面都会被JspServlet翻译成一个Servlet源文件,然后将源文件编译为.class文件。Servlet源文件和.class文件都放在“Tomcat安装目录/work/Catalina/localhost/应用名/”目录下。由JSP文件翻译成的Servlet类带有包名,包名为org.apache.jsp,因此simple.jsp生成的Servlet源文件和.class文件的目录结构如图1所示。
图1 JSP文件翻译页面
从图1可以看出,simple.jsp文件被翻译成的class文件和Servlet源文件分别是simple_jsp.class和simple_jsp.java。打开simple_jsp.java文件,查看翻译后的Servlet源代码,如下所示。
1 package org.apache.jsp;
2 import javax.servlet.*;
3 import javax.servlet.http.*;
4 import javax.servlet.jsp.*;
5 public final class simple_jsp extends org.apache.jasper.runtime.HttpJspBase
6 implements org.apache.jasper.runtime.JspSourceDependent {
7 private static final javax.servlet.jsp.JspFactory _jspxFactory =
8 javax.servlet.jsp.JspFactory.getDefaultFactory();
9 private static java.util.Map<java.lang.String, java.lang.Long> _jspx_dependants;
10 private javax.el.ExpressionFactory _el_expressionfactory;
11 private org.apache.tomcat.InstanceManager _jsp_instancemanager;
12 public java.util.Map<java.lang.String, java.lang.Long> getDependants() {
13 return _jspx_dependants;
14 }
15 public void _jspInit() {
16 _el_expressionfactory = _jspxFactory.getJspApplicationContext(
17 getServletConfig().getServletContext()).getExpressionFactory();
18 _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory
19 .getInstanceManager(getServletConfig());
20 }
21 public void _jspDestroy() {
22 }
23 public void _jspService(
24 final javax.servlet.http.HttpServletRequest request,
25 final javax.servlet.http.HttpServletResponse response)
26 throws java.io.IOException, javax.servlet.ServletException {
27 final javax.servlet.jsp.PageContext pageContext;
28 javax.servlet.http.HttpSession session = null;
29 final javax.servlet.ServletContext application;
30 final javax.servlet.ServletConfig config;
31 javax.servlet.jsp.JspWriter out = null;
32 final java.lang.Object page = this;
33 javax.servlet.jsp.JspWriter _jspx_out = null;
34 javax.servlet.jsp.PageContext _jspx_page_context = null;
35 try {
36 response.setContentType("text/html");
37 pageContext = _jspxFactory.getPageContext(this, request, response,
38 null, true, 8192, true);
39 _jspx_page_context = pageContext;
40 application = pageContext.getServletContext();
41 config = pageContext.getServletConfig();
42 session = pageContext.getSession();
43 out = pageContext.getOut();
44 _jspx_out = out;
45 out.write("<html>\r\n");
46 out.write("<head>\r\n");
47 out.write("<title>A simple jsp</title>\r\n");
48 out.write("</head>\r\n");
49 out.write("<body>\r\n");
50 out.write(" 当前访问时间是 :\r\n");
51 out.write(" ");
52 out.print(new java.util.Date().toLocaleString());
53 out.write("\r\n");
54 out.write("</body>\r\n");
55 out.write("</html>");
56 } catch (java.lang.Throwable t) {
57 if (!(t instanceof javax.servlet.jsp.SkipPageException)) {
58 out = _jspx_out;
59 if (out != null && out.getBufferSize() != 0)
60 try {
61 out.clearBuffer();
62 } catch (java.io.IOException e) {
63 }
64 if (_jspx_page_context != null)
65 _jspx_page_context.handlePageException(t);
66 else
67 throw new ServletException(t);
68 }
69 } finally {
70 _jspxFactory.releasePageContext(_jspx_page_context);
71 }
72 }
73 }
从上面的代码可以看出,simple.jsp文件翻译后的Servlet类名为simple_jsp,它没有实现Servlet接口,但继承了org.apache.jasper.runtime.HttpJspBase类。在Tomcat源文件中查看HttpJspBase类的源代码,具体如下所示:
1 import java.io.IOException;
2 import javax.servlet.ServletConfig;
3 import javax.servlet.ServletException;
4 import javax.servlet.http.HttpServlet;
5 import javax.servlet.http.HttpServletRequest;
6 import javax.servlet.http.HttpServletResponse;
7 import javax.servlet.jsp.HttpJspPage;
8 import org.apache.jasper.compiler.Localizer;
9 public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {
10 private static final long serialVersionUID = 1L;
11 protected HttpJspBase() {
12 }
13 public final void init(ServletConfig config)
14 throws ServletException
15 {
16 super.init(config);
17 jspInit();
18 _jspInit();
19 }
20 public String getServletInfo() {
21 return Localizer.getMessage("jsp.engine.info");
22 }
23 public final void destroy() {
24 jspDestroy();
25 _jspDestroy();
26 }
27 public final void service(HttpServletRequest request, HttpServletResponse response)
28 throws ServletException, IOException
29 {
30 _jspService(request, response);
31 }
32 public void jspInit() {
33 }
34 public void _jspInit() {
35 }
36 public void jspDestroy() {
37 }
38 protected void _jspDestroy() {
39 }
40 public abstract void _jspService(HttpServletRequest request,
41 HttpServletResponse response)
42 throws ServletException, IOException;
43 }
从HttpJspBase源代码中可以看出,HttpJspBase类是HttpServlet的一个子类。由此可见,simple_jsp类就是一个Servlet。接下来,针对HttpJspBase源代码中的一部分内容进行详细讲解,具体如下:
(1)第13~19行代码定义了init()方法,该方法使用final进行修饰,并且其内部调用了jspInit()和_jspInit()方法。由此说明,在JSP页面所生成的Servlet不能覆盖这两个方法。但是,如果要在JSP页面完成Servlet的init()方法的功能,只能覆盖jspInit()和_jspInit()这两个方法中的任何一个。同样,如果要在JSP页面中完成Servlet的destroy()方法,则只能覆盖jspDestroy()和_jspDestroy()两个方法中的任何一个。
(2)第27~31行代码定义了service()方法,在service()方法中调用了_jspService()方法,也就是说在用户访问JSP文件时,会调用HttpJspBase类中的service()方法来响应用户的请求。根据Java的多态性的特征,在service()方法中会调用simple_jsp类中实现的_jspService()方法用来响应用户的请求。simple.jsp的内容都被翻译到了simple_jsp类的_jspService()方法中,对于HTML标签以及文本直接调用out.write()方法将其作为字符串输出,而对于<% %>中的Java代码所输出生成的字符串则插入到它在JSP模板元素中所对应的位置,因此我们能在浏览器中看到simple.jsp文件的结果。