前一个小节,我们大概介绍了如何创建以及访问一个JSP程序,这一小节我们就深入了解一下JSP程序的本质,以及JSP运行的原理。
1.1、JSP本质
前面说过,JSP程序本质上就是一个Servlet程序,为什么这么说呢???这是因为JSP程序运行过程中,会被编译成Servlet程序,最终代码执行的也是这个编译之后的Servlet程序。
那么要如何确定JSP程序被编译成了Servlet程序呢???我们可以打开IDEA中Tomcat的工作目录,IDEA中的Tomcat工作目录是在【C:\Users\用户名称\AppData\Local\JetBrains\IDEA版本\tomcat】,注意啦,这个目录要看你IDEA的版本,版本不同对应的目录也不同,比如我是IntelliJIdea2023.2版本,目录也就是IntelliJIdea2023.2,例如:我电脑中的目录就是【C:\Users\zhuyb\AppData\Local\JetBrains\IntelliJIdea2023.2\tomcat】。
如下所示:
进入tomcat目录里面,你可能会看到多个以UUID字符串的目录,这些目录其实都是一个Web应用程序,只不过是IDEA临时创建的运行目录,找到我们前一小节创建的JSP应用程序。
进入目录之后,此时可以看到一个work目录,这个work目录是不是很熟悉,Tomcat安装目录下面不也有一个work目录,work目录的作用就是存放JSP程序被编译之后生成的Servlet程序。
进入work目录下的目录,找到我们的那个工程目录【work\Catalina\localhost\servlet\org\apache\jsp】,jsp目录就是保存JSP程序被编译成Servlet程序后的内容。
从上图中,就可以看出,我们之前创建的HelloWorld.jsp文件,最终是被编译成了HelloWorld_jsp.java文件。
编译之后的JSP文件的命名规则是:XXX_jsp.java、XXX_jsp.class,其中XXX是原先的JSP文件名称。
到这里,我们就可以解释,为什么JSP程序本质上Servlet程序啦。
1.2、编译之后的JSP源代码
打开上面编译之后的HelloWorld_jsp.java文件,查看里面的源代码,如下所示:
/*
* Generated by the Jasper component of Apache Tomcat
* Version: Apache Tomcat/8.5.98
* Generated at: 2024-02-24 07:57:25 UTC
* Note: The last modified time of this file was set to
* the last modified time of the source file after
* generation to assist with modification tracking.
*/
package com.gitcode.servlet;
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,
org.apache.jasper.runtime.JspSourceImports {
private static final JspFactory _jspxFactory =
JspFactory.getDefaultFactory();
private static java.util.Map<String, Long> _jspx_dependants;
private static final java.util.Set<String> _jspx_imports_packages;
private static final java.util.Set<String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<String, Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
throws java.io.IOException, ServletException {
final String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
final PageContext pageContext;
HttpSession session = null;
final ServletContext application;
final ServletConfig config;
JspWriter out = null;
final Object page = this;
JspWriter _jspx_out = null;
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("<html>\r\n");
out.write("<head>\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <h3>Hello JSP</h3>\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
} catch (Throwable t) {
if (!(t instanceof SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
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);
}
}
}
从编译之后的JSP源代码中可以看出,我们自己写的JSP文件,编译之后会继承自HttpJspBase类,并且实现JspSourceDependent、JspSourceImports两个接口。
其中HttpJspBean类,它是继承自HttpServlet类,如下图所示:
从这就再次说明了,JSP程序本质上就是一个Servlet程序,因为最终都是继承HttpServlet类的。既然JSP是Servlet,那么就会有service方法啦,JSP中不叫做service方法,而是叫做_jspService()方法,如下所示:
_jspService()方法中,可以看到JSP中编写的那些HTML内容,这些内容采用out.write()方法,嵌入到编译之后的Servlet程序里面。
到此,你是不是对JSP程序有了更加深刻的理解了呢???JSP专门用于HTML界面展示,而Servlet专门用于和服务端进行数据交互。
今天就到这里,未完待续~~