专业编程基础技术教程

网站首页 > 基础教程 正文

《JSP》第02节:JSP程序的本质及其运行原理

ccvgpt 2024-07-17 17:53:50 基础教程 13 ℃

#精品长文创作季#

前一个小节,我们大概介绍了如何创建以及访问一个JSP程序,这一小节我们就深入了解一下JSP程序的本质,以及JSP运行的原理。

《JSP》第02节: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专门用于和服务端进行数据交互。

今天就到这里,未完待续~~

Tags:

最近发表
标签列表