jsp编译后的servlet类会是怎样的呢?他们之间有着什么样的映射关系?在探讨jsp与servlet之间的关系时先看一个简单的helloworld.jsp编译成helloworld.java后会是什么样。
①helloworld.jsp
<%@ page contenttype="text/html; charset=utf-8" language="java" %>
<%
out.println("helloworld");
%>
②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 _jspx_dependants;
public java.util.map 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=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("\r\n");
out.write("
\r\n");out.write("
\r\n");out.write("
\r\n");out.write(" \r\n");
out.write("
\r\n");out.println("helloworld");
out.write("\r\n");
out.write("\r\n");
out.write("\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类型的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指令标签。