JSP编译后的Servlet类会是怎样的呢他们之间有着什么样的映射关系在探讨JSP与Servlet之间的关系时先看一个简单的HelloWorld.jsp编译成HelloWorld.java后会是什么样。
①HelloWorld.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>HelloWorld</TITLE>
</HEAD>
<BODY>
<%
out.println("HelloWorld");
%>
</BODY>
</HTML>
②HelloWorld_jsp.java
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class HelloWorld_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
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;
try {
response.setContentType("text/html; charset=gb2312");
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 HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n");
out.write("<HTML>\r\n");
out.write(" <HEAD>\r\n");
out.write(" <TITLE>HelloWorld</TITLE>\r\n");
out.write(" </HEAD>\r\n");
out.write("<BODY>\r\n");
out.println("HelloWorld");
out.write("\r\n");
out.write("</BODY>\r\n");
out.write("</HTML>\r\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try { out.clearBuffer(); } catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
经过前面介绍的语法解析及使用访问者模式对HelloWorld.jsp文件编译成相应的HelloWorld_jsp.java文件可以看到Servlet类名是由jsp文件名_jsp拼成。再往下看HelloWorld_jsp.java文件的详细内容类包名默认为org.apache.jsp默认有三个导入“import javax.servlet.*;import javax.servlet.http.*;import javax.servlet.jsp.*;”。
接下去是真正的类主体jsp生成的java类都必须继承org.apache.jasper.runtime.HttpJspBase这个类的结构图如下继承了HttpServlet是为了将HttpServlet的所有功能都继承下来另外又实现HttpJspPage接口定义了一个JSP类的Servlet的核心处理方法_jspService除此之外还有_jspInit和_jspDestroy用于在jsp初始化和销毁时执行这些方法其实都是由Servlet的service、init、destroy方法间接去调用所以jsp生成servlet主要就是实现这三个方法。
除了继承HttpJspBase外还需实现org.apache.jasper.runtime.JspSourceDependent接口这个接口只有一个返回Map<String,Long>类型的getDependants()方法Map的键值分别为资源名和最后修改时间这个实现主要是为了记录某些依赖资源是否过时依赖资源可能是page指令导入的也可能是标签文件引用等。在生成servlet时如果jsp页面做了上述依赖的话则会在servlet类中添加一个static块static块会将资源及最后修改时间添加到Map中。
在jsp类servlet处理过程中会依赖很多资源比如我要操作会话的话就需要此次访问的HttpSession对象比如我要操作Context容器级别的对象就要ServletContext对象再比如我要获取servlet配置信息就要ServletConfig对象最后还需要一个输出对象用于在处理过程中将内容输出。这些对象都在核心方法_jspService中使用作为servlet类要获取这些对象其实非常简单因为这些本身就属于servlet属性有相关方法直接获取。但这里因为JSP有自己的标准所以必须按照它的标准去实现。
具体的JSP标准是怎样的首先为了方便JSP的实现提供一个统一的工厂类JspFactory用于获取不同的资源其次由于按照标准规定不能直接使用servlet上下文所以需要定义一个PageContext类封装servlet上下文最后同样按照标准需要定义一个输出类JspWriter封装servlet的输出。所以可以看到PageContext对象通过JspFactory获取其他ServletContext对象、ServletConfig对象、HttpSession对象及JspWriter则通过PageContext对象获取。通过这些对象再加上前面章节语法解析得到的语法树对象再利用访问者模式对语法树遍历就可以生成核心处理方法_jspService了。
上面只是介绍了最简单的一个jsp页面转变成servlet的过程旨在说明jsp到servlet转化的原理实际上需要处理很多jsp指令标签。