Tomcat源码系列4--Tomcat中Servlet处理流程
本次谈一下Servlet的处理流程,不当之处请各位指正。
在经过了「Tomcat启动」,「Tomcat请求处理」等一系列流程后,程序运行到org.apache.catalina.core.ApplicationFilterChain.ApplicationFilterChain.InternalDoFilter()方法,这时会调用servlet的service()方法。
void internalDoFilter(ServletRequest request, ServletResponse response) throws
IOException, ServletException{
// filter処理的代码省略。
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
} else {
servlet.service(request, response);
}
}
程序到了这里,会出现三种情况,分别对应着对Servlet,JSP,和静态资源的处理。
对于Servlet,直接调用开发人员自己编写的#service()方法。
对于静态资源(HTML页面),servlet会是org.apache.catalina.servlets.DefaultServlet类的一个实例。会调用DefaultServlet的#service()方法。
对于jsp页面, servlet会是 org.apache.jasper.servlet.JspServlet 类的一个实例。调用JspServlet类的#service()方法。
下面我们将分别从HTML请求和JSP请求两个方面展开来讨论程序运行情况。
一. HTML页面处理过程
以下是HTML请求处理的时序图
1.从上示流程图可知首先HttpServlet#service(ServletRequest req, ServletResponse res)被调用。
(javax.servlet.http.HttpServlet.service)
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
}
2. HttpServlet.service(HttpServletRequest req, HttpServletResponse resp)
被调用。
(javax.servlet.http.HttpServlet.service)
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//此方法将判断请求的servlet的方法是doGet()、doPost()或是其他的方法,
//此次调用以doGet(req, resp)方法为例。
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
}
}
3. defaultServlet# doGet()被调用。
(org.apache.catalina.servlets.DefaultServlet.doGet)
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//请求服务的资源,包括数据内容
try {
serveResource(request, response, true);
}
}
在serveResource()方法中会进行request资源的处理和response资源的创建。通过以上步骤,HTML页面的请求处理完毕,将逐层返回调用,最后将在浏览器端得到HTML页面的显示。
二. JSP页面处理过程
以下是JSP请求处理的时序图
1. 从上图可知首先HttpServlet#service(ServletRequest req,ServletResponse res) 被调用。
(javax.servlet.http.HttpServlet.service)
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
2. 由于是JSP资源,接下来JspServlet#service会被调用。
(org.apache.jasper.servlet.JspServlet.service)
public void service (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// 预编译模式,如果是预编译模式,只是对JSP进行编译,不会返回页面执行结果
boolean precompile = preCompile(request);
// 继续JSP请求
serviceJspFile(request, response, jspUri, null, precompile);
}
}
3.jspServlet#serviceJspFile(request, response, jspUri, null, precompile)将被调用。
(org.apache.jasper.servlet.JspServlet.service)
private void serviceJspFile(HttpServletRequest request,
HttpServletResponse response, String jspUri,
Throwable exception, boolean precompile)
throws ServletException, IOException {
JspServletWrapper wrapper =
(JspServletWrapper) rctxt.getWrapper(jspUri);
wrapper.service(request, response, precompile);
}
4.jspServletWrapper#service(HttpServletRequest request,
HttpServletResponse response,boolean precompile)方法被调用。此方法将进行三个步骤,编译,加载,处理服务请求
(org.apache.jasper.servlet.JspServletWrapper.service)
public void service(HttpServletRequest request,
HttpServletResponse response,
boolean precompile)
throws ServletException, IOException, FileNotFoundException {
try {
// (1) 编译 ☆1
if (options.getDevelopment() || firstTime ) {
synchronized (this) {
if (firstTime) {
firstTime = false;
}
// The following sets reload to true, if necessary
ctxt.compile();
}
} else {
if (compileException != null) {
// Throw cached compilation exception
throw compileException;
}
}
// (2) 加载servlet类
getServlet();
// (3) 处理服务请求 ☆2
if (theServlet instanceof SingleThreadModel) {
synchronized (this) {
theServlet.service(request, response);
}
} else {
theServlet.service(request, response);
}
☆1 编译
1.首先jspCompiler#compile()将被调用。
(org.apache.jasper.JspCompilationContext.compile)
public void compile() throws JasperException, FileNotFoundException {
// 创建"jspCompiler"的实例
createCompiler();
// 判断是否过期,即是否需要重新编译,这个方法中主要用源代码和编译后的文件的修改时间等因素进行判断
if (jspCompiler.isOutDated()) {
try {
jspCompiler.removeGeneratedFiles();
jspLoader = null;
// 执行编译
jspCompiler.compile();
}
2.Compiler#compile(true)将被调用。
(org.apache.jasper.JspCompilationContext.compile)
public void compile()
throws FileNotFoundException, JasperException, Exception
{
compile(true);
}
3.Compiler#compile(boolean compileClass)被调用。
(org.apache.jasper.JspCompilationContext.compile)
public void compile(boolean compileClass)
throws FileNotFoundException, JasperException, Exception
{
compile(compileClass, false);
}
4.Compile#compile(boolean compileClass, boolean jspcMode)被调用
(org.apache.jasper.JspCompilationContext.compile)
public void compile(boolean compileClass, boolean jspcMode)
throws FileNotFoundException, JasperException, Exception
{
if (errDispatcher == null) {
this.errDispatcher = new ErrorDispatcher(jspcMode);
}
try {
String[] smap = generateJava();
if (compileClass) {
generateClass(smap);//生成class文件
}
5. 程序运行到此时Compiler#generateJava()被调用。
(org.apache.jasper.compiler.Compiler.generateJava())
protected String[] generateJava() throws Exception {
// generate servlet .java file
Generator.generate(writer, this, pageNodes);
writer.close();
writer = null;
}
generateJava()方法的作用是将JSP文件转换为Java文件。该部分的功能是由Jasper实现的。
在生成java文件以后,将调用generateClass()方法,将java文件编译成class文件。
☆2 处理服务请求
1.JspServletWrapper#service(request, response)被调用。
2. HttpServlet#service(ServletRequest request, ServletResponse response)被调用。
(javax.servlet.http.HttpServlet.service)
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
}
3.HttpJspBase#Servlet(HttpServletRequest request,
HttpServletResponse response)被调用。
(org.apache.jasper.runtime.HttpJspBase.service)
public final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
_jspService(request, response);
}
4. _jspService(request, response)被调用。实际上是调用由JSP转换为java程序的_jspService()方法,Servlet请求结束后,程序逐层回调,得到返回结果。