JSP 的本質原理解析:"編寫的時候是JSP,心裡想解讀的是 java 原始碼"

2023-04-30 18:00:18

JSP 的本質原理解析:"編寫的時候是JSP,心裡想解讀的是 java 原始碼"

@


每博一文案

活明白的人,一生只做好了這兩件事:
每個瞬間都充滿了選擇和承擔,就算面前是一座獨木橋,也必須選擇是前進後退,亦或是留在原地此時此刻你所經歷的一切。
這是過往無數個選擇後的結果,哪些小的選擇匯聚在了一起,最終成了我們今天的時光。
其實,活明白的人一生只做好了兩件事看,一是選擇,二是承擔。常聽人爭論選擇和努力哪個更重要。
其實,努力並不是選擇的對比面,而是擁有選擇的基本條件。不努力的人往往連選擇的資格都沒有,努力是為了,
更好的選擇。正如馬伯庸說:所謂的選擇只是努力所賦予人的一種資格。
所有的一夜成名,剎那的焰火,實際是過往今年默默努力埋下的伏筆,因此這裡說的選擇不是投機取巧的小聰明。
而是積澱後的深思熟慮。常言道:選擇不對,努力白費。
李蘭娟院士在為了汕頭大學的畢業學生送上給予時,表示:不管是在生活還是事業上,有很多外部變化和環境因素是我們
所無法選擇的。我們可以選擇的是在每一個人生路口要做出怎樣的選擇,以及如何勇敢的前行。
我們所有的人都在共用一段歲月,卻在不同的選擇中分道揚鑣,因為人生最重要的除了努力還有選擇。
當你被生活的疲憊裹挾著,被環境的艱難牽制著,這時你要麼被生活牽著鼻子走,要麼接收痛苦與打擊,抗住一場場暴風雪,主動迎擊一地雞毛的瑣碎,你要做的不是抱怨生活的不公,而是邁出步子,奪下生活的主導權,做出選擇了。
一種選擇就是一種代價,不同的選擇造就了不同的人生,人打從生下來就面臨著很多種未知的可能。
未來都是一張白紙,任由自己去上色作畫。有些人完成地很好,也有人畫得一團糟,人生的一萬種可能好的,不好的
都是有認真選擇好。
每一支畫筆,在每次選擇時都要堅持原則,便是對自己的人生負責。

                                            —————— 《一禪心靈廟語》

1. JSP 概述

JSP(全稱JavaServer Pages),sun公司主導的一種動態網頁技術,JSP在伺服器端執行,可以響應使用者端的請求,根據請求內容動態的生成HTML、XML或其他格式檔案的Web網頁然後返回請求者。在JSP頁面可以嵌入Java程式碼,JSP檔案在執行時會被其編譯器轉換成更原始的Servlet程式碼,然後再由Java編譯器來編譯成能快速執行的二進位制機器碼。

2.特點:

  1. 能以模板化的方式簡單、高效地新增動態網頁內容。
  2. 可利用JavaBean和標籤庫技術複用常用的功能程式碼。
  3. 有良好的工具支援。
  4. 繼承了Java語言的相對易用性。
  5. 繼承了Java的跨平臺優勢,實現「一次編寫,處處執行」。
  6. 頁面中的動(控制變動內容的部分)/靜(內容不需變動的部分)區域以分散但又有序的形式組合在一起,方便開發維護。
  7. 可與其它企業級Java技術相互配合。JSP可以只專門負責頁面中的資料呈現,實現分層開發。

3.JSP頁面組成:

在 HTML 頁面檔案中加入 Java 程式段和 JSP 標籤,即可構成一個 JSP 頁檔案,JSP 頁面由 5 種元素組合而成。
普通的 HTML 標記符。

  1. JSP 標籤,如指令標籤、動作標籤。
  2. 變數和方法的宣告。
  3. Java 程式段。
  4. Java 表示式。

2. 第一個 JSP 程式

我的第一個JSP程式:

這裡給一個小的建議,大家在閱讀如下,文章時,可以帶著一個這樣的問題:JSP 是什麼 ? 去閱讀文章,有助於後面的內容上的閱讀理解。

WEB-INF目錄之外建立一個 index.jsp檔案,然後這個檔案中沒有任何內容。注意 了:我們對於這個jsp 檔案當中並沒有編寫任何的內容,一個字元,一個標點都可以,就是一個空白的。如下顯示的:

將上面的專案部署之後,啟動伺服器,開啟瀏覽器,存取以下地址 http://127.0.0.1:8080/servlet14/index.jsp 我們部署的專案的路徑:具體顯示如下。

重點: 實際上我們存取的以上的這個:index.jsp,底層執行的是:index_jsp.class 這個java程式。我們可以在我們本地電腦上存取到該生成是 index_jsp.class,index_jsp.java的檔案,該生成的對應的.class,.java檔案所在的路徑是在我們啟動 Tomcat 伺服器當中提示的,一個 CATALINA_BASE路徑下的 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62

這個index.jsp會被tomcat翻譯生成index_jsp.java檔案,然後tomcat伺服器又會將 index_jsp.java編譯生成index_jsp.class檔案。存取index.jsp,實際上執行的是index_jsp.class中的方法。

如下是我們存取 index.jsp 在我本地電腦上生成的 CATALINA_BASE的 路徑下的,index_jsp.java,index_jsp.class 的檔案。 C:\Users\huo\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\4b6bbfbb-d520-498b-b8f2-090a7ad68f62\work\Catalina\localhost\servlet14\org\apache\jsp

如下是我們的一個 index.jsp(空內容) 被翻譯為 index_jsp.java 檔案的內容如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 03:20:48 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 org.apache.jsp;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.jsp.*;

public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {

  private static final jakarta.servlet.jsp.JspFactory _jspxFactory =
          jakarta.servlet.jsp.JspFactory.getDefaultFactory();

  private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

  private static final java.util.Set<java.lang.String> _jspx_imports_packages;

  private static final java.util.Set<java.lang.String> _jspx_imports_classes;

  static {
    _jspx_imports_packages = new java.util.HashSet<>();
    _jspx_imports_packages.add("jakarta.servlet");
    _jspx_imports_packages.add("jakarta.servlet.http");
    _jspx_imports_packages.add("jakarta.servlet.jsp");
    _jspx_imports_classes = null;
  }

  private volatile jakarta.el.ExpressionFactory _el_expressionfactory;
  private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
    return _jspx_dependants;
  }

  public java.util.Set<java.lang.String> getPackageImports() {
    return _jspx_imports_packages;
  }

  public java.util.Set<java.lang.String> getClassImports() {
    return _jspx_imports_classes;
  }

  public jakarta.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 jakarta.servlet.http.HttpServletRequest request, final jakarta.servlet.http.HttpServletResponse response)
      throws java.io.IOException, jakarta.servlet.ServletException {

    if (!jakarta.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      final java.lang.String _jspx_method = request.getMethod();
      if ("OPTIONS".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        return;
      }
      if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允許 GET、POST 或 HEAD。Jasper 還允許 OPTIONS");
        return;
      }
    }

    final jakarta.servlet.jsp.PageContext pageContext;
    jakarta.servlet.http.HttpSession session = null;
    final jakarta.servlet.ServletContext application;
    final jakarta.servlet.ServletConfig config;
    jakarta.servlet.jsp.JspWriter out = null;
    final java.lang.Object page = this;
    jakarta.servlet.jsp.JspWriter _jspx_out = null;
    jakarta.servlet.jsp.PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html");
      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(' ');
      out.write(' ');
    } catch (java.lang.Throwable t) {
      if (!(t instanceof jakarta.servlet.jsp.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);
    }
  }
}

3. JSP 的本質就是 Servlet

從上述我們編寫的第一jsp 程式,index.jsp 空內容的,index.jsp存取的時候,會自動翻譯生成index_jsp.java,會自動編譯生成index_jsp.class,那麼index_jsp 這就是一個類。

從圖中我們可以看到,該index.jsp 翻譯的 index_jsp.java 類是繼承了 extends org.apache.jasper.runtime.HttpJspBase 類的,我們查閱其 Tomcat 10 的 HttpJspBase 原始碼如下:

如下是 HttpJspBase 的原始碼內容

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jasper.runtime;

import java.io.IOException;

import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.HttpJspPage;

import org.apache.jasper.Constants;
import org.apache.jasper.compiler.Localizer;

/**
 * This is the super class of all JSP-generated servlets.
 *
 * @author Anil K. Vijendran
 */
public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {

    private static final long serialVersionUID = 1L;

    protected HttpJspBase() {
    }

    @Override
    public final void init(ServletConfig config)
        throws ServletException
    {
        super.init(config);
        jspInit();
        _jspInit();
    }

    @Override
    public String getServletInfo() {
        return Localizer.getMessage("jsp.engine.info", Constants.SPEC_VERSION);
    }

    @Override
    public final void destroy() {
        jspDestroy();
        _jspDestroy();
    }

    /**
     * Entry point into service.
     */
    @Override
    public final void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        _jspService(request, response);
    }

    @Override
    public void jspInit() {
    }

    public void _jspInit() {
    }

    @Override
    public void jspDestroy() {
    }

    protected void _jspDestroy() {
    }

    @Override
    public abstract void _jspService(HttpServletRequest request,
                                     HttpServletResponse response)
        throws ServletException, IOException;
}

而 HttpServlet 是 extends 了 GenericServlet 抽象類,而 GenericServlet 抽象類是 實現了 Servlet 介面的。

所以:一個index_jsp類就是一個Servlet類。總結就是:一個 index.jsp 檔案會被翻譯為一個 index_jsp.java 類,而 該 翻譯的 index_jsp.java類是 繼承 了 org.apache.jasper.runtime.HttpJspBase 類的,而 org.apache.jasper.runtime.HttpJspBase 類 是繼承了 HttpServlet 類的。這下邏輯就清晰了,JSP 就是 Servlet

  • JSP 的生命週期和Servlet的生命週期完全相同。完全就是一個東西。沒有任何區別。
  • JSP和servlet一樣,都是單例的。(假單例,真單例是:其類的構造器是 private 私有化的,而Servlte 的構造器不是 private 私有化的,是公開的。所以為假單例),想要了解更多的,大家可以移步至: