上一篇文章我们了解到,Tomcat使用DefaultServlet处理所有的静态资源(回复静态两个字查看)。本篇我们来看看,jsp文件是如何被处理的。
和DefaultServlet类似,jsp的处理也不需要我们单独配置,而是在conf/web.xml中做为全局配置存在。其对应的处理类为JspServlet,用于处理所有的Jsp请求。其url-pattern是*.jsp和*.jspx。
但与DefaultServlet所不同的是,jsp文件的特殊性。
我们知道jsp和html类似,最终页面展现是根据页面内的元素渲染生成。
本质上,jsp是一种特殊的Servlet文件,其在收到请求时,将页面内的各类元素编译,在工作目录生成一个Servlet文件。
而这一个工作过程,对我们使用者是透明的。整个过程在后台执行完,走和Servlet类似的流程返回浏览器。
整个工作的流程可以归纳成下列的步骤:
对相应的jsp页面的请求被JspServlet拦截
执行JspServlet的service方法
获取指定的Jsp的Compiler
判断jsp文件对应的Servlet文件是否成生,是否过期
初次请求或者已过期,生成Servlet文件
调用生成的jsp的Servlet类内的_jspService方法
后续的流程和普通的Servlet一致
对照上面的流程,只有同jsp生成Servlet这一步,对于我们较为陌生。下面从代码的角度,来看下Tomcat内部的工作过程。
public void compile() throws JasperException, FileNotFoundException {
createCompiler();
if (jspCompiler.isOutDated()) {
jspCompiler.removeGeneratedFiles();
jspLoader = null;
jspCompiler.compile();
jsw.setReload(true);
jsw.setCompilationException(null);
}
在判断文件是初始请求或者已经过期后,调用编译器,生成Servlet文件。生成的文件内容类似下面的样子
文件位于Tomcat的work/Catalina目录下,对应的abc.jsp文件,生成的Servlet文件名则为abc_jsp.java
创建编译器的部分如下,可见其使用的是Eclipse的JDTCompiler
对应的生成代码如下:
public static void generate(ServletWriter out, Compiler compiler,
Node.Nodes page) throws JasperException {
Generator gen = new Generator(out, compiler);
if (gen.isPoolingEnabled) {
gen.compileTagHandlerPoolList(page);
}
gen.generateCommentHeader(); //生成文件头注释
if (gen.ctxt.isTagFile()) {
JasperTagInfo tagInfo = (JasperTagInfo) gen.ctxt.getTagInfo();
gen.generateTagHandlerPreamble(tagInfo, page);
if (gen.ctxt.isPrototypeMode()) {
return;
}
gen.generateXmlProlog(page);
gen.fragmentHelperClass.generatePreamble();
page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
gen.methodsBuffered, gen.fragmentHelperClass));
gen.generateTagHandlerPostamble(tagInfo);
} else {
gen.generatePreamble(page);
gen.generateXmlProlog(page);
gen.fragmentHelperClass.generatePreamble();
page.visit(gen.new GenerateVisitor(gen.ctxt.isTagFile(), out,
gen.methodsBuffered, gen.fragmentHelperClass));
gen.generatePostamble();
}
}
看上面的红色部分,整个编译生成的过程,就是完整的输出字符流到文件的过程。不信你看下面的内容,就是生成文件头注释的代码。
private void generateCommentHeader() {
out.println("/*");
out.println(" * Generated by the Jasper component of Apache Tomcat");
out.println(" * Version: " + ctxt.getServletContext().getServerInfo());
out.println(" * Generated at: " + timestampFormat.format(new Date()) +
" UTC");
out.println(" * Note: The last modified time of this file was set to");
out.println(" * the last modified time of the source file after");
out.println(" * generation to assist with modification tracking.");
out.println(" */");
}
// Generate class declaration //生成类的声明部分
out.printin("public final class ");
out.print(servletClassName);
out.print(" extends ");
out.println(pageInfo.getExtends());
out.printin(" implements org.apache.jasper.runtime.JspSourceDependent,");
out.println();
out.printin(" org.apache.jasper.runtime.JspSourceImports");
if (!pageInfo.isThreadSafe()) {
out.println(",");
out.printin(" javax.servlet.SingleThreadModel");
}
out.println(" {");
out.pushIndent();
整个请求流程,我画了一个时序图,在后台回复jsp时序图,即可获取下载链接。
如果感觉有用,欢迎转发到朋友圈,让更多朋友了解。
扫描下方二维码,关注本公众号,获取更多精彩内容。