- Tomcat在默认的web.xml中配置了org.apache.jasper.servlet.JspServlet, 用于处理所有的 .jsp和 .jspx结尾的请求
- JspServlet的实现就是运行编译时的入口
当用户发起请求时首先会到达JspServlet.service()。那么我们就从JspServlet.service()方法开始跟踪源码吧。
public void service (HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//获取请求方式
String method = request.getMethod();
//校验请求类型
if (!"GET".equals(method) && !"POST".equals(method) && !"HEAD".equals(method) &&
!DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
Localizer.getMessage("jsp.error.servlet.invalid.method"));
return;
}
//获取jsp路径
String jspUri = jspFile;
if (jspUri == null) {
jspUri = (String) request.getAttribute(Constants.JSP_FILE);
}
if (jspUri == null) {
jspUri = (String) request.getAttribute(
RequestDispatcher.INCLUDE_SERVLET_PATH);
}
}
//判断当前是否是预编译请求
boolean precompile = preCompile(request);
//获取JspServletWrapper,并调用 wrapper的service方法。
serviceJspFile(request, response, jspUri, precompile);
接下来看JspServletWrapper的service方法。
public void service(HttpServletRequest request,
HttpServletResponse response,
boolean precompile)
throws ServletException, IOException, FileNotFoundException {
Servlet servlet;
try {
/*
* 若为开发环境,或者第一次调用则编译
*/
if (options.getDevelopment() || firstTime ) {
synchronized (this) {
firstTime = false;
// 编译流程
ctxt.compile();
}
}
/*
* (2) 查询加载并实例化编译后的Servlet
*/
servlet = getServlet();
/*
* (3) 更新上次使用时间
*/
if (unloadAllowed) {
synchronized(this) {
if (unloadByCount) {
if (unloadHandle == null) {
unloadHandle = ctxt.getRuntimeContext().push(this);
} else if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
ctxt.getRuntimeContext().makeYoungest(unloadHandle);
lastUsageTime = System.currentTimeMillis();
}
} else {
if (lastUsageTime < ctxt.getRuntimeContext().getLastJspQueueUpdate()) {
lastUsageTime = System.currentTimeMillis();
}
}
}
}
/*
* (4) 调用jspServlet执行请求
*/
if (servlet instanceof SingleThreadModel) {
synchronized (this) {
servlet.service(request, response);
}
} else {
servlet.service(request, response);
}
}
}
编译流程的源码解析
Jasper的编译流程主要包括代码生成和编译两部分
public void compile() throws JasperException, FileNotFoundException {
//创建编译器,运行时编译的编译器为JDTCompiler
createCompiler();
if (jspCompiler.isOutDated()) {
jspCompiler.removeGeneratedFiles();
jspLoader = null;
//编译jsp文件
jspCompiler.compile();
jsw.setReload(true);
jsw.setCompilationException(null);
}
}
public void compile(boolean compileClass, boolean jspcMode)
throws FileNotFoundException, JasperException, Exception {
//将 jsp 文件编译成一个servlet的 .java 文件。
String[] smap = generateJava();
File javaFile = new File(ctxt.getServletJavaFileName());
Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
javaFile.setLastModified(jspLastModified.longValue());
//将 servlet 从 .java 文件编译为 .class 文件
generateClass(smap);
}