无代码无真相。这里我们来看看,一个简单的jsp页面,在被服务器解释执行时,到底发生了怎么样的转换。
在你的tomcat根目录下的webapps目录下,新建一个test目录。在新建的test目录里面,创建一个WEB-INF目录。将tomcat根目录下的conf目录里的web.xml目录拷贝到/webapps/test/WEB-INF目录下,这里的web.xml的可以把<web-app>标签里面的内容全部删除,因为conf里面已经声明过了。然后在/webapps/test目录下新建一个txt文档,并把名称改为MyJsp.jsp,注意后缀名是.jsp。
完成上面操作后的目录结构如下:
wbapps - test -
-MyJsp.jsp
-WEB-INF -
- web.xml
启动tomcat,在地址栏中输入http://127.0.0.1:8080/test/MyJsp.jsp。浏览器里一片空白,这是自然的,因为我们的MyJsp.jsp文件本身就什么内容也没有。
在tomcat的work目录中找到test目录,它的位置是/work/Catalina/localhos/下面,然后依次顺着目录找下去,在/work/Catalina/localhos/org/apache/jsp目录下面,你会发现一个MyJsp_jsp.java文件。这个文件就是MyJsp.jsp文件被翻译成的java文件。它并不是在jsp文件名后面加了Servlet,而是直接把"."变成"_"作为类名。
打开MyJsp_jsp.java文件:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static java.util.List _jspx_dependants;
public Object getDependants() {
return _jspx_dependants;
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html");
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;
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
呵呵,原来即使是一个空白的jsp文件,也照样会生成这么长的一段代码的。没有看到servlet的字样?翻翻tomcat的api,你就会发现,MyJsp_jsp的父类HttpJspBase已经实现了javax.servlet.Servlet接口的。
下面我们在MyJsp.jsp文件中加入以下HTML代码:
<html>
<body>
This is my first JSP!
</body>
</html>
刷新一下浏览器,我们发现,MyJsp_jsp.java中, _jspService()方法中,增加了以下代码:
out.write("<html>\r\n");
out.write("<body>\r\n");
out.write("\tThis is my first JSP!\r\n");
out.write("</body>\r\n");
out.write("</html>");
原来,所有的HTML代码都要用out.write()方法输出出来的啊。所以可以想象,如果使用纯粹的Servlet编写一个复杂的web页面,将是一件多么麻烦的事情。
下面,我们来实验将jsp里面插入代码段的情况。教材里面说jsp里面的java代码段有两种形式,一种是<%! %>包围,一种是<% %>包围。(当然取值符<%= %>里面的表达式里也可以有“相当长”的java代码段,这个我们暂时不予考虑)
<%! %>一般用来表示全局变量的声明,以及方法的声明,而<% %>则是普通的可执行程序段。什么意思呢?用代码来说话!
在MyJsp.jsp文件里面加入以下代码:
<%!
double PI = 3.14159;
int plus(int a, int b){
return a+b;
}
%>
<%
String hello = "Hello World!";
%>
再次刷新浏览器,查看MyJsp_jsp.java代码:
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class MyJsp_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
double PI = 3.14159; //<%! %>里面的变量成为成员变量
int plus(int a, int b){//<%! %>里面的方法成为成员方法
return a+b;
}
private static java.util.List _jspx_dependants;
public Object getDependants() {
return _jspx_dependants;
}
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
try {
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html");
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;
String hello = "Hello World!"; //<%%>里面的代码段
out.write("\r\n");
out.write("\r\n"); //HTML代码段
out.write("<html>\r\n");
out.write("<body>\r\n");
out.write("\tThis is my first JSP!\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
out.clearBuffer();
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
}
} finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
我们发现,<%! %>里面声明的变量成为了MyJsp_jsp的成员变量,里面声明的方法成为了MyJsp_jsp类的成员方法,而<% %>的代码则被填充到了_jspService()方法中。
注意<% %>中的代码在_jspService()方法中的填充位置。这里的代码是被填充在一大堆对象的初始化之后,因此, _jspService()方法中的这一大堆对象就可以在<% %>里面直接拿来使用,这些对象就是所谓JSP的内置对象,除了_jspFactory对象没什么用处外,这里有八个内置对象,按照声明顺序依次是:request、response、pageContext、session、application、config、out、page。这些内置对象除了可以直接在你的<% %>代码段中使用外,还会在你试图声明和它们同名的对象时,优雅地给你报出一个错误。(废话)