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

Sea發表於2023-04-30

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是什麼了 ?

  • JSP是java程式。(JSP本質還是一個Servlet)
  • JSP是:JavaServer Pages的縮寫。(基於Java語言實現的伺服器端的頁面。)
  • Servlet是JavaEE的13個子規範之一,那麼JSP也是JavaEE的13個子規範之一。
  • JSP是一套規範。所有的web容器/web伺服器都是遵循這套規範的,都是按照這套規範進行的“翻譯”
  • 每一個web容器/web伺服器都會內建一個JSP翻譯引擎。這裡所謂的翻譯指的是: 將我們訪問的 jsp 檔案翻譯成 對應的以.java 字尾結尾的Java檔案,再將 該 java 檔案編譯生成 .class 檔案。

問題:JSP第一次訪問的時候是比較慢的,為什麼?

同問: 為什麼大部分的運維人員在給客戶演示專案的時候,為什麼提前先把所有的jsp檔案先訪問一遍。

因為我們第一次比較麻煩,需要進行一個專案的資源的載入。

  • 第一次比較麻煩的地方:
    • 要把jsp檔案翻譯生成java原始檔
    • java原始檔要編譯生成class位元組碼檔案
    • 然後透過class去建立servlet物件
    • 然後呼叫servlet物件的init方法
    • 最後呼叫servlet物件的service方法。
  • 第二次就比較快了,為什麼?
    • 因為第二次直接呼叫單例servlet物件的service方法即可。

注意點:

實際上我們對於 JSP 進行錯誤除錯的時候,還是要直接開啟JSP檔案對應翻譯生成的 java檔案,檢查其中 java程式碼。

開發JSP的最高境界: 眼前編寫的是JSP程式碼,但是我們心裡,腦袋裡面當中呈現的是 java程式碼。

JSP既然本質上是一個Servlet,那麼JSP和Servlet到底有什麼區別呢?

職責不同:

  • Servlet的職責是什麼:收集資料。(Servlet的強項是後端的邏輯處理,業務處理,然後連結資料庫,獲取/收集資料。)
  • JSP的職責是什麼:展示資料。(JSP的強項是做 前端資料的展示)

4. JSP 的基礎語法

上面我們學習,瞭解到了 JSP 的本質原理,下面我們就來學習一下有關 JSP的基礎語法吧

4.1 在 JSP 檔案中直接編寫文字

在 jsp 檔案中直接編寫文字,都會自動被翻譯到哪裡?

如下我們實驗一下,我們在上一個 index.jsp 檔案當中,直接編寫一個 “Hello Wrold” 。當我們瀏覽器端訪問該 index.jsp 檔案時,瀏覽器端有是顯示一個則樣的效果,而其本質中 “Hello Wrold” 又是被翻譯到了,成了什麼,翻譯到了哪裡。

在這裡插入圖片描述

在這裡插入圖片描述

如下我們檢視對應 index.jsp 翻譯的 index_jsp.java 檔案的內容如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 06:10:10 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("Hello World");
    } 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);
    }
  }
}

在這裡插入圖片描述

翻譯到 servlet類的 service() 方法的 out.write("翻譯到這裡") ,直接翻譯到雙引號裡,被java程式當做普通字串列印輸出到瀏覽器。

補充一點: 解決瀏覽器端顯示的亂碼問題 ?

JSP的page指令(這個指令後面再詳細說,這裡先解決一下中文亂碼問題),解決響應時的中文亂碼問題:

  • 透過page指令來設定響應的內容型別,在內容型別的最後面新增:charset=UTF-8
    • <%@page contentType="text/html;charset=UTF-8"%>,表示響應的內容型別是text/html,採用的字符集UTF-8
    • <%@page import="java.util.List,java.util.ArrayList"%> 表示到對應的 包資訊

在JSP中編寫的HTML CSS JS程式碼,這些程式碼對於JSP來說只是一個普通的字串。但是JSP把這個普通的字串一旦輸出到瀏覽器,瀏覽器就會對HTML CSS JS進行解釋執行。展現一個效果。舉例如下:

<%@page contentType="text/html; chatSet=UTF-8" %>

<html>

<head>
    <title>my first JSP page</title>

    <script type="text/javascript">
        function sayHello() {
            window.alert("你好 JSP")
        }
    </script>
</head>
<bady>
    <h1>My first JSP page</h1>
    <input type="button" value="hello jsp" onclick="sayHello()" />

    fajsi ffasdfda
    fasd
    afjsfi9

    Sytstem.out.println("hello JSP");
</bady>
</html>

在這裡插入圖片描述

訪問 index.jsp 顯示的效果如下:

在這裡插入圖片描述

對應翻譯的java 的內容如下:注意:都是被翻譯到了 service()方法體當中去了

 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; 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("\r\n");
      out.write("<html>\r\n");
      out.write("\r\n");
      out.write("<head>\r\n");
      out.write("    <title>my first JSP page</title>\r\n");
      out.write("\r\n");
      out.write("    <script type=\"text/javascript\">\r\n");
      out.write("        function sayHello() {\r\n");
      out.write("            window.alert(\"你好 JSP\")\r\n");
      out.write("        }\r\n");
      out.write("    </script>\r\n");
      out.write("</head>\r\n");
      out.write("<bady>\r\n");
      out.write("    <h1>My first JSP page</h1>\r\n");
      out.write("    <input type=\"button\" value=\"hello jsp\" onclick=\"sayHello()\" />\r\n");
      out.write("\r\n");
      out.write("    fajsi ffasdfda\r\n");
      out.write("    fasd\r\n");
      out.write("    afjsfi9\r\n");
      out.write("\r\n");
      out.write("    Sytstem.out.println(\"hello JSP\");\r\n");
      out.write("</bady>\r\n");
      out.write("</html>");

      
    } 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);
    }
  }
}

4.2 在JSP中編寫Java程式 <% %> 與 <%! %>

4.2.1 <% %>

<% 
 
// java語句;
// 在這裡編寫Java程式,編寫 Java語句。
// 既然是編寫 Java語句,自然要遵循Java的語法規範。
// 在這裡編寫的java語句,會被翻譯到 service() 方法體內
%>

在這個<% java語句; %>符號當中編寫的被視為java程式,被翻譯到Servlet類的 service方法體內部。舉例如下:在這裡插入圖片描述


<%
    System.out.println("Hello World");
%>


<%
    System.out.println("Hello JSP");
%>

<%
    System.out.println("你好世界");
%>

訪問 瀏覽器前端訪問:demo01.jsp 效果

在這裡插入圖片描述

在這裡插入圖片描述

因為我們只在 demo01.jsp 檔案當中編寫了,Java程式碼,並沒有編寫其他的,而且我們的Java程式碼編寫的僅僅只是一個簡單的輸出到控制檯上的一個輸出語句而已了。所以前端瀏覽器是沒有任何的顯示內容的。

如下是 對應我們訪問 demo01.jsp 檔案翻譯為 demo01_jsp.java 檔案的原始碼資訊如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 06:43:13 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 demo01_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; 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(' ');
      out.write(' ');
      out.write(' ');
      out.write('\r');
      out.write('\n');

    System.out.println("Hello World");

      out.write("\r\n");
      out.write("\r\n");
      out.write("\r\n");

    System.out.println("Hello JSP");

      out.write("\r\n");
      out.write("\r\n");

    System.out.println("你好世界");

      out.write('\r');
      out.write('\n');
    } 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);
    }
  }
}

在這裡插入圖片描述

<% %>的使用的注意事項:

  • 這裡你要細心點,你要思考,在<% %>這個符號裡面寫java程式碼的時候,你要時時刻刻的記住你正在“sevice() 方法體”當中寫程式碼,方法體中可以寫什麼,不可以寫什麼,你心裡是否明白呢?

方法體當中不可以,定義靜態程式碼塊,以及程式碼塊,不可以再定義方法了,不可以被定義成員變數 (被許可權修飾符修飾的變數。)

在這裡插入圖片描述

如果在 jsp 當中所編寫的 Java程式存在錯誤,瀏覽器端是會抱 500 錯誤的。如下所示:以 5 開頭的一般都是伺服器端的錯誤,這時候,我們一般是透過檢視其中翻譯的 java 程式當中翻譯的錯誤了。

在這裡插入圖片描述
在這裡插入圖片描述

  • 在service方法當中編寫的程式碼是有順序的,方法體當中的程式碼要遵循自上而下的順序依次逐行執行。
  • service方法當中不能寫靜態程式碼塊,不能寫方法,不能定義成員變數。。。。。。
  • 在同一個JSP當中 <% %> 這個符號可以出現多個。

在這裡插入圖片描述

4.2.2 <%! %>

<%!
	// 在這個符號當中編寫的java程式會自動翻譯到service方法之外。
    // service() 方法體之外是什麼,自然就是在 Servlet 類裡面了。
%>
  • <%! %>在這個符號當中編寫的java程式會自動翻譯到service方法之外。簡單的說就是: Servlet 類當中,service() 方法外,那不就是 成員變數,方法,程式碼塊了。

舉例如下:

在這裡插入圖片描述

啟動部署,訪問執行如下

在這裡插入圖片描述

  • 這個語法很少用,為什麼?並且也不建議使用,因為在service方法外面寫靜態變數和例項變數,都會存線上程安全問題,因為JSP就是servlet,servlet是單例的,多執行緒併發的環境下的,這個靜態變數和例項變數一旦有修改操作,必然會存線上程安全問題。

4.3 透過JSP當中的 <%= %>向瀏覽器前端輸入 Java變數

怎麼向瀏覽器上輸出一個java變數。

錯誤方式: 直接在 jsp 當中編寫對應變數名,因為我們的在 JSP檔案當中直接編寫的文字的內容是被翻譯到了 service() 方法體當中去了被 out.write("</html>") 包裹的。所以翻譯的不是對應變數的值,而是對應變數的名(以字串的形式翻譯到了瀏覽器端了)。舉例如下:

在這裡插入圖片描述

在這裡插入圖片描述

正確的方式一:不推薦

<% 
String name = “jack”; 
out.write("name = " + name); 
%>

注意: 以上程式碼中的 out是JSP的九大內建物件之一。可以直接拿來用。當然,必須只能在 service()方法內部使用(因為 JSP的內建物件是定義在 service( ) 方法體內的,所以其作用域也僅僅只是在 service() 方法體內部,才有效的)。

舉例:測試

在這裡插入圖片描述

  • 如果向瀏覽器上輸出的內容中沒有“java程式碼”,例如:輸出本身就是一個固定的字串,可以直接在jsp中編寫,不需要寫到<% %> 這裡。因為僅僅只是一個輸出字串的話,其在JSP直接編寫,輸出的就是字串 的。

  • 如果輸出的內容中含有“java程式碼當中的變數的值”,這個時候可以使用以下語法格式:

    <%=變數名%>  // 在=的後面編寫要輸出的內容。
    

    <%= %> 注意:在=的後面編寫要輸出的內容。

    舉例:

在這裡插入圖片描述

<%= %> 這個符號會被翻譯到哪裡?最終翻譯成什麼?

如下是:對應 demo02.jsp 檔案翻譯成的對應的 demo02_jsp.java 檔案的原始碼如下:

/*
 * Generated by the Jasper component of Apache Tomcat
 * Version: Apache Tomcat/10.0.12
 * Generated at: 2023-04-21 11:55:08 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 demo02_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;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(' ');
      out.write("\r\n");
      out.write("\r\n");

    String name = "張三";

      out.write("\r\n");
      out.write("\r\n");
      out.print(name);
      out.write("\r\n");
      out.write("<br>\r\n");
      out.print( 1 + 2);
      out.write("\r\n");
      out.write("\r\n");
    } 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);
    }
  }
}

在這裡插入圖片描述

翻譯成了這個java程式碼: out.print();

翻譯到service方法當中了。

什麼時候使用<%=%> 輸出呢?輸出的內容中含有java的變數,輸出的內容是一個動態的內容,不是一個死的字串。如果輸出的是一個固定的字串,直接在JSP檔案中編寫即可。

4.4 在JSP 中的專業註釋

  • 在JSP中如何編寫JSP的專業註釋
<%--JSP的專業註釋,不會被翻譯到java原始碼當中。--%> 不會翻譯對應的資訊。該註解當中的內容會被忽略而不翻譯的。

<!--這種註釋屬於HTML的註釋,這個註釋資訊仍然會被翻譯到java原始碼當中,不建議。-->  翻譯到HTML當中作為 html 的
註解存在。

4.5 JSP基礎語法總結:

  • JSP中直接編寫普通字串
翻譯到service方法的out.write("這裡")
  • <%%>
翻譯到service方法體內部,裡面是一條一條的java語句。
  • <%! %>
翻譯到service方法之外。
  • <%= %>
翻譯到service方法體內部,翻譯為:out.print();
  • <%@page contentType="text/html;charset=UTF-8"%>
page指令,透過contentType屬性用來設定響應的內容型別以及字符集編碼的設定。

JSP檔案的副檔名必須是xxx.jsp嗎 ?

  • .jsp文件的副檔名是可以配置的。不是固定的。

  • 在CATALINA_HOME/conf/web.xml,在這個檔案當中配置jsp檔案的副檔名。

在這裡插入圖片描述

在這裡插入圖片描述

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

xxx.jsp檔案對於 Tomcat 小貓咪來說,只是一個普通的文字檔案,web容器會將xxx.jsp檔案最終生成java程式,最終呼叫的是java物件相關的方法,真正執行的時候,和jsp檔案就沒有關係了。

5. JSP的指令

指令的作用: 指導JSP的翻譯引擎如何工作(指導當前的JSP翻譯引擎如何翻譯JSP檔案。)

指令包括哪些呢?

  • include指令: 包含指令,在JSP中完成靜態包含,很少用了。(這裡不講)
  • taglib指令: 引入標籤庫的指令。這個到JJSTL標籤庫的時候再學習。現在先不管。
  • page指令: 目前重點學習一個page指令。

指令的使用語法是什麼?

<%@指令名  屬性名=屬性值  屬性名=屬性值  屬性名=屬性值....%>
// 這裡我們只學習 page 指令所以是:
<%@page  屬性名=屬性值  屬性名=屬性值  屬性名=屬性值....%>

如下是:page指令當中一些 常用的屬性

 <%@page session="true|false" %>
 true表示啟用JSP的內建物件session,表示一定啟動session物件。沒有session物件會建立。
 如果沒有設定,預設值就是session="true"
 session="false" 表示不啟動內建物件session。當前JSP頁面中無法使用內建物件session。
 session 表示的是會話機制。

在這裡插入圖片描述

 <%@page contentType="text/json" %>
 contentType屬性用來設定響應的內容型別
 但同時也可以設定字符集。
 <%@page contentType="text/json;charset=UTF-8" %>
 <%-- 編碼也可以單獨分開設定如下--%>
 <%@page pageEncoding="UTF-8" %>
 pageEncoding="UTF-8" 表示設定響應時採用的字符集。

在這裡插入圖片描述

 <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %> 可以多個包一起匯入
 <%@page import="java.util.*" %> 也可以單個導包
 import語句,導包。

在這裡插入圖片描述

 <%@page errorPage="/error.jsp" %>
 當前頁面出現異常之後,跳轉到error.jsp頁面。
 errorPage屬性用來指定出錯之後的跳轉位置。
 
  <%@page isErrorPage="true" %>
 表示啟用JSP九大內建物件之一:exception
 預設值是false。 
 
 errorPage,和 isErrorPage 一般是連著一起使用的,因為 errorPage 表示錯誤跳轉到指定的頁面,但是不會有錯誤提示,而 isErrorPage 就是為了解決這個沒有錯誤提示的問題。
 

舉例:

在這裡插入圖片描述

測試:訪問 demo03.jsp 該檔案當中存在一個 java 程式的一個 null 指標訪問的異常。當發生錯誤時,錯誤不處理,也不列印錯誤資訊,而是直接跳轉到 error.jsp 檔案

在這裡插入圖片描述

在這裡插入圖片描述

可以使用 JSP當中的九大內建物件之一的 : **java.lang.Throwable exception ** 。

注意:想要使用該 java.lang.Throwable exception 內建物件必須,配置相關的指令:

<%--
 在錯誤頁面可以啟用JSP九大內建物件,exception
 exeption內建物件就是剛剛發生的異常物件
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有兩個引數一個是為:false,true :true 表示開啟 isErrorPage ,false 表示禁用,預設不設定也是禁用狀態的--%>

error.jsp 的內容上的編寫如下


<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<%--
 在錯誤頁面可以啟用JSP九大內建物件,exception
 exeption內建物件就是剛剛發生的異常物件
--%>
<%@page isErrorPage="true" %>
<%--isErrorPage 有兩個引數一個是為:false,true :true 表示開啟 isErrorPage ,false 表示禁用,預設不設定也是禁用狀態的--%>

<html>
<head>
    <title>程式錯誤</title>
</head>
<body>
<h2>網路繁忙,稍後再試</h2>

<%
    // 配置了 isErrorPage 內建的異常物件則可以
    // 列印異常堆疊資訊,輸出到後臺控制檯上
    // exception 是JSP 九大內建物件之一。
    exception.printStackTrace();
%>


</body>
</html>

在這裡插入圖片描述

6. JSP的九大內建物件

注意:所謂的JSP的九大內建物件指的是:在 service() 方法體當中的區域性的物件變數,service() 方法體外是無法使用的。

在這裡插入圖片描述

  • jakarta.servlet.jsp.PageContext pageContext 頁面作用域

  • jakarta.servlet.http.HttpServletRequest request 請求作用域

  • jakarta.servlet.http.HttpSession session 會話作用域

  • jakarta.servlet.ServletContext application 應用作用域

    • pageContext < request < session < application
    • 以上四個作用域都有:setAttribute、getAttribute、removeAttribute方法。
    • 以上作用域的使用原則:儘可能使用小的域。
  • java.lang.Throwable exception

  • jakarta.servlet.ServletConfig config

  • java.lang.Object page (其實是this,當前的servlet物件)

  • jakarta.servlet.jsp.JspWriter out (負責輸出)

  • jakarta.servlet.http.HttpServletResponse response (負責響應)

思考一個問題:如果我只用JSP這一個技術,能不能開發web應用?

  • 當然可以使用JSP來完成所有的功能。因為JSP就是Servlet,在JSP的<%%>裡面寫的程式碼就是在service方法當中的,所以在<%%>當中完全可以編寫JDBC程式碼,連線資料庫,查詢資料,也可以在這個方法當中編寫業務邏輯程式碼,處理業務,都是可以的,所以使用單獨的JSP開發web應用完全沒問題。
  • 雖然JSP一個技術就可以完成web應用,但是不建議,還是建議採用servlet + jsp的方式進行開發。這樣都能將各自的優點發揮出來。JSP就是做資料展示。Servlet就是做資料的收集。(JSP中編寫的Java程式碼越少越好。)一定要職責分明。

7. 使用Servlet + JSP完成oa專案的改造

關於 上次的 oa 專案的詳細內容,大家可以先移步至:??? Servlet註解的使用,簡化配置 以及,使用模板方法設計模式最佳化oa專案_ChinaRainbowSea的部落格-CSDN部落格 ,以及: 使用“純”Servlet做一個單表的CRUD操作_ChinaRainbowSea的部落格-CSDN部落格建議大家先,跳過去,看一看,有助於後面該文章內容上的閱讀理解。

  • 使用Servlet處理業務,收集資料。 使用JSP展示資料。

  • 將之前原型中的html檔案,全部修改為jsp,然後在jsp檔案頭部新增page指令(指定contentType防止中文亂碼),將所有的JSP直接複製到web目錄下。

  • 完成所有頁面的正常流轉。(頁面仍然能夠正常的跳轉。修改超連結的請求路徑。)

    • <%=request.getContextPath() %> 在JSP中動態的獲取應用的根路徑。
  • Servlet中連線資料庫,查詢所有的部門,遍歷結果集。

    • 遍歷結果集的過程中,取出部門編號、部門名、位置等資訊,封裝成java物件。
    • 將java物件存放到List集合中。
    • 將List集合儲存到request域當中。
    • 轉發forward到jsp。
  • 在JSP中:

    • 從request域當中取出List集合。
    • 遍歷List集合,取出每個部門物件。動態生成tr。

模組對應的包目錄如下:

在這裡插入圖片描述


DeptServlet

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import com.RainbowSea.bean.Dept;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

// 可以使用模糊查詢 @WebServlet("/dept/*")
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        String servletPath = request.getServletPath();  // 獲取到瀏覽器當中的uri

        // 雙重的判斷,一個是 session 會話域要存在,其次是 會話域當中儲存了名為 "username" 的資訊
        if ("/dept/list".equals(servletPath)) {
            doList(request, response);
        } else if ("/dept/detail".equals(servletPath)) {
            doDetail(request, response);
        } else if ("/dept/delete".equals(servletPath)) {
            doElete(request, response);
        } else if ("/dept/save".equals(servletPath)) {
            doSave(request, response);
        } else if ("/dept/modify".equals(servletPath)) {
            doModify(request, response);
        }


    }


    /**
     * 修改部門資訊
     *
     * @param request
     * @param response
     */
    private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影響資料庫的行數
        int count = 0;


        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");


        try {
            // 1. 註冊驅動,連線資料庫
            connection = DBUtil.getConnection();

            // 2. 獲取到運算元據庫的物件,預編譯sql語句,sql測試
            String sql = "update dept set dname = ?,loc = ? where depton = ?";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充佔位符,真正執行sql語句
            // 從下標 1開始
            preparedStatement.setString(1, dname);
            preparedStatement.setString(2, loc);
            preparedStatement.setString(3, deptno);

            count = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 4. 釋放資源,最後使用的優先被釋放
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 更新成功
            // 跳轉到部門列表頁面(部門列表表面是透過java程式動態生成的,所以還需要再次執行另一個Servlet)
            // 轉發是伺服器內部的操作,“/” 不要加專案名
            // request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 最佳化使用重定向,自發前端(需要指明專案名)
            response.sendRedirect(request.getContextPath() + "/dept/list");

        }
    }


    /**
     * 儲存部門資訊
     *
     * @param request
     * @param response
     */
    private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException {

        request.setCharacterEncoding("UTF-8");

        // 獲取到前端的資料,建議 name 使用複製
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");

        // 連線資料庫,新增資料
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影響資料庫的行數
        int count = 0;

        try {
            // 1. 註冊驅動,連線資料庫
            connection = DBUtil.getConnection();

            // 2. 獲取運算元據庫物件,預編譯sql語句,Sql測試
            String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充佔位符, 真正執行sql語句,
            // 注意: 佔位符的填充是從 1 開始的,基本上資料庫相關的起始下標索引都是從 1下標開始的
            preparedStatement.setString(1, deptno);
            preparedStatement.setString(2, dname);
            preparedStatement.setString(3, loc);

            // 返回影響資料庫的行數
            count = preparedStatement.executeUpdate();

            // 5.釋放資源
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection, preparedStatement, null);
        }

        // 儲存成功,返回部門列表頁面
        if (count == 1) {
            // 這裡應該使用,重定向
            // 這裡用的轉發,是伺服器內部的,不要加專案名
            //request.getRequestDispatcher("/dept/list/").forward(request, response);

            // 重定向
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }


    /**
     * 透過部門刪除部門
     *
     * @param request
     * @param response
     */
    private void doElete(HttpServletRequest request, HttpServletResponse response) throws IOException {

        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        // 獲取到傳送資料
        String deptno = request.getParameter("deptno");

         /*
        根據部門編號刪除資訊,
        刪除成功,跳轉回原來的部門列表頁面
        刪除失敗,跳轉刪除失敗的頁面
         */

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        // 記錄刪除資料庫的行數
        int count = 0;


        // 連線資料庫進行刪除操作

        try {
            // 1.註冊驅動,連線資料庫
            connection = DBUtil.getConnection();

            // 開啟事務(取消自動提交機制),實現可回滾
            connection.setAutoCommit(false);

            // 2. 預編譯sql語句,sql測試
            String sql = "delete from dept where depton = ?"; // ? 佔位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充佔位符,真正的執行sql語句
            preparedStatement.setString(1, deptno);
            // 返回影響資料庫的行數
            count = preparedStatement.executeUpdate();
            connection.commit();  // 手動提交資料
        } catch (SQLException e) {
            // 遇到異常回滾
            if (connection != null) {
                try {
                    // 事務的回滾
                    connection.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
            }
            throw new RuntimeException(e);
        } finally {
            // 4. 釋放資源
            // 因為這裡是刪除資料,沒有查詢操作,所以 沒有 ResultSet 可以傳null
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 刪除成功
            // 仍然跳轉到部門列表頁面
            // 部門列表頁面的顯示需要執行另外一個Servlet,怎麼辦,可以使用跳轉,不過這裡最後是使用重定向
            // 注意:轉發是在伺服器間的,所以不要加“專案名” 而是 / + web.xml 對映的路徑即可
            //request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 最佳化:使用重定向機制 注意: 重定向是自發到前端的位址列上的,前端所以需要指明專案名
            // 注意: request.getContextPath() 返回的根路徑是,包含了 "/" 的
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }


    /**
     * 透過部門編號,查詢部門的詳情
     *
     * @param request
     * @param response
     */
    private void doDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 獲取到部門編號
        String dno = request.getParameter("dno");
        Dept dept = new Dept();

        // 獲取到部門編號,獲取部門資訊,將部門資訊收集好,然後跳轉到JSP做頁面展示


        try {
            // 2. 連線資料庫,根據部門編號查詢資料庫
            // 1.註冊驅動,連線資料庫
            connection = DBUtil.getConnection();

            // 2. 預編譯SQL語句,sql要測試
            String sql = "select dname,loc from dept where depton = ?";  // ? 佔位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充佔位符,真正執行sql語句
            preparedStatement.setString(1, dno);
            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            while (resultSet.next()) {
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                // 封裝物件(建議使用咖啡豆,因為只有一個物件)
                dept.setDeptno(dno);
                dept.setDname(dname);
                dept.setLoc(loc);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5. 釋放資源
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        // 這個咖啡豆只有一個,所以不需要袋子,只需要將這個咖啡豆放到request請求域當中,
        // 用於對應的 jsp顯示
        request.setAttribute("dept", dept);
        //String sign = request.getParameter("f");

        /*if("m".equals(sign)) {

            // 轉發:多個請求為一個請求(位址列不會發生改變)
            // 注意: 該路徑預設是從 web 開始找的 / 表示 web
            // 轉發到修改頁面
            request.getRequestDispatcher("/edit.jsp").forward(request,response);
        } else if("d".equals(sign)) {
            // 跳轉到詳情頁面
            request.getRequestDispatcher("/detail.jsp").forward(request,response);
        }*/

        // 或者最佳化
        // 注意 無論是轉發還是重定向都是從 “/” 開始的
        // request.getParameter()拿到的是 f=edit,還是f=detail 就是跳轉到的哪個頁面
        //<a href="<%=request.getContextPath()%>/dept/detail?f=edit&dno=<%=dept.getDeptno()%>">修改</a>
        //<a href="<%=request.getContextPath()%>/dept/detail?f=detail&dno=<%=dept.getDeptno()%>">詳情</a>
        String forward = "/" + request.getParameter("f") + ".jsp";
        request.getRequestDispatcher(forward).forward(request, response);
    }


    /**
     * 連線資料庫,查詢所有的部門資訊,將部門資訊收集好,然後跳轉到JSP頁面展示
     *
     * @param request
     * @param response
     */
    private void doList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 建立一個集合List 儲存查詢到的資訊
        List<Dept> depts = new ArrayList<Dept>();


        try {
            // 連線資料庫,查詢所有部門:
            // 1. 註冊驅動,獲取連線
            connection = DBUtil.getConnection();
            // 2. 獲取運算元據庫物件,預編譯sql語句
            String sql = "select depton as det,dname,loc from dept"; // 在mysql中測試一下是否正確
            preparedStatement = connection.prepareStatement(sql);

            // 3. 執行sql語句
            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            while (resultSet.next()) {
                String det = resultSet.getString("det");  // 有別名要使用別名
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                Dept dept = new Dept(det, dname, loc);

                // 將部門物件放到List集合當中
                depts.add(dept);

            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {

            // 5. 關閉資源
            DBUtil.close(connection, preparedStatement, resultSet);
        }


        // 查詢到資料,將資料提交給 list.jsp 顯示資料
        // 將集合儲存的資料放到請求域當中,用於其他Servlet 使用 jsp 也是Servelt
        request.setAttribute("depList", depts);

        // 轉發(注意不要重定向),重定向無法共用 request 請求域當中的資料
        // 轉發路徑,/ 預設是從 web 目錄開始找的
        request.getRequestDispatcher("/list.jsp").forward(request, response);
    }
}

UserServlet

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


@WebServlet({"/user/login", "/user/exit"})
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // 獲取到瀏覽器位址列上的URL路徑
        String servletPath = request.getServletPath();

        if ("/user/login".equals(servletPath)) {
            doLogin(request, response);
        } else if ("/user/exit".equals(servletPath)) {
            doExit(request, response);
        }


    }

    private void doExit(HttpServletRequest request, HttpServletResponse response) throws IOException {
    }

    protected void doLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // 一個使用者登入驗證的方式:驗證使用者名稱和密碼是否正確
        // 獲取使用者名稱和密碼
        // 前端提交是資料是:username=111&password=fads
        // 注意:post 提交的資料是在請求體當中,而get提交的資料是在請求行當中

        boolean success = false;  // 標識登入成功

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        String exempt = request.getParameter("exempt");

        // 連線資料庫驗證使用者名稱和密碼
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 1. 獲取連線,註冊驅動
            connection = DBUtil.getConnection();

            // 2. 獲取運算元據物件,預編譯sql語句, ? 佔位符不要加,“”,'' 單雙引號,成了字串了,無法識別成佔位符了。
            String sql = "select username,password from t_user where username = ? and password = ?";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充佔位符,真正執行sql語句
            preparedStatement.setString(1, username);
            preparedStatement.setString(2, password);

            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            // 只有一條結果集
            if (resultSet.next()) {
                // 登入成功
                success = true;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5. 關閉資源,最後使用的最先關閉,
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        // 登入成功與否
        if (success) {
            response.sendRedirect(request.getContextPath() + "/dept/list");
        } else {
            // 失敗,跳轉到失敗頁面
            response.sendRedirect(request.getContextPath() + "/error.jsp");
        }


    }
}

所以的 html 頁面替換為了對應的 jsp 檔案

add.html --> add.jsp

<%@page contentType="text/html; charset=utf-8" %>


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>新增部門</title>
</head>

<body>
<h1>新增部門</h1>
<form action="<%=request.getContextPath()%>/dept/save" method="post">
  部門編號: <input type="text" name="deptno" /><br>
  部門名稱: <input type="text" name="dname" /><br>
  部門位置: <input type="text" name="loc" /><br>
  <input type="submit" value="儲存" />
</form>
</body>

</html>

detail.html --> detail.jsp

<%@ page import="com.RainbowSea.bean.Dept" %>
<%@page contentType="text/html; charset=UTF-8" %>


<%
  // 獲取到請求域當中的資料,
  Dept dept = (Dept) request.getAttribute("dept");
%>

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>部門詳情</title>
</head>
<body>
<h1>部門詳情</h1>
部門編號: <%=dept.getDeptno()%> <br>
部門名稱: <%=dept.getDname()%><br>
部門位置: <%=dept.getLoc()%><br>

<input type="button" value="後退" onclick="window.history.back()"  />
</body>
</html>

edit.html --> edit.jsp

<%@ page import="com.RainbowSea.bean.Dept" %>
<%@page contentType="text/html; charset=UTF-8" %>

<!DOCTYPE html>
<html lang="en">


<%
  // 獲取到儲存到請求域當的 部門資訊
  Dept dept = (Dept) request.getAttribute("dept");
%>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>修改部門</title>
</head>

<body>
<h1>修改部門</h1>
<form action="<%=request.getContextPath()%>/dept/modify" method="post">
  <!-- readonly 表示只讀,不可修改的作用 -->
  部門編號: <input type="text" name="deptno" value="<%=dept.getDeptno()%>" readonly /><br>
  部門名稱: <input type="text" name="dname" value="<%=dept.getDname()%>" /><br>
  部門位置: <input type="text" name="loc" value="<%=dept.getLoc()%>" /><br>
  <input type="submit" value="修改" />
</form>

</body>

</html>

error.html --> error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page session="false" %>
<html>
<head>
    <title>登入失敗</title>
</head>
<body>

登入失敗,密碼或賬號錯誤: <a href="<%=request.getContextPath()%>/index.jsp">請重新登入</a>

</body>
</html>

index.html --> index.jsp

<%@page contentType="text/html; charset=UTF-8" %>
<%--表示訪問 jsp 的時候不生成 session 物件--%>
<%@page session="false" %>  <%--該指令不是警用jsp內建物件當中的 session--%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>歡迎使用OA系統</title>
</head>
<body>
<%--<!--注意:對應前端的資源獲取基本上都是要加專案名的,並且要"/"開始-->--%>
<%--這種方式不好,將 專案名寫死了,--%>
<%--<a href="/servlet09/dept/list/">檢視部門列表</a>--%>
<%--使用request.getContextPath() 動態獲取專案名的根路徑帶 / 的,
注意哪裡可以加 空格,哪裡不能加空格--%>
<%--<a href="<%=request.getContextPath()          %>/dept/list">檢視部門列表</a>--%>

<%--<%=request.getContextPath()%> &lt;%&ndash;out.print(request.getContextPath()) 獲取到該專案的根路徑帶有/的&ndash;%&gt;--%>


    <h1>Login in</h1>
    <hr>
    <form action="<%=request.getContextPath()%>/user/login" method="post">
        username: <input type="text" name="username" /><br>
        password: <input type="password" name="password" /> <br>
        免十天登入 <input type="checkbox" name="exempt" value="true" /> <br>
        <input type="submit" value="login" />
    </form>
</body>
</html>

list.html ---> list.jsp

<%@ page import="com.RainbowSea.bean.Dept" %>
<%@ page import="java.util.List" %>  <%--這是導包在jSP 當中,並翻譯為import匯入對於的報--%>
<%@page contentType="text/html; charset=utf-8" %>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>部門列表頁面</title>
</head>
<script type="text/javascript">
    function del(dno) {
        // 彈出確認框,使用者點選確定,返回true,點選取消返回false
        var ok = window.confirm("親,刪了不可恢復哦!");
        if (ok) {
            // 傳送請求進行刪除資料的操作
            // 在js程式碼當中如何傳送請求給伺服器
            //
            // document.location.href="請求路徑"
            // document.location = "請求路徑"
            // window.location.href = "請求路徑"
            // window.location = "請求路徑"

            // 注意是根據所傳的部門編號刪除資料的
            document.location.href = "<%=request.getContextPath()%>/dept/delete?deptno=" + dno;

        }
    }
</script>

<body>
<h1 align="center">部門列表</h1>
<%--顯示登入的使用者名稱資訊--%>
<h3><%=session.getAttribute("username")%></h3>  <%--注意這裡使用的是 jsp 內建的 session 物件所以不可以,不要把 session 禁用了。--%>

<a href="<%=request.getContextPath()%>/user/exit">安全退出系統(手動清除 session 會話資訊)</a>

<table border="1px" align="center" width="50%">
    <tr>
        <th>序號</th>
        <th>部門編號</th>
        <th>部門名稱</th>
    </tr>

    <%
        // 獲取到 request 域當中的資料,並取出來,我們知道是什麼型別的資料,直接強制轉換
        List<Dept> depList = (List<Dept>) request.getAttribute("depList");

        int i = 0;
        // 迴圈遍歷List,取出資料
        for (Dept dept : depList) {
            // 在後臺輸出測試
            //System.out.println(dept.getDname());
            // 在瀏覽器顯示 測試
            //out.write(dept.getDname() + "<br>");


    %>
    <%--    瀏覽器顯示測試--%>
    <%--    <%=dept.getDname()%>--%>


    <tr>
        <td><%=++i%>
        </td>
        <td><%=dept.getDeptno()%>
        </td>
        <td><%=dept.getLoc()%>
        </td>
        <td>
            <!-- javascript:void(0)保持<a>超連結的格式,同時不會發生跳轉
                我只是希望使用者點選該超連結的時候執行一段js程式碼,不進行頁面的跳轉 -->
            <a href="javascript:void(0)" onclick="del(<%=dept.getDeptno()%>)">刪除</a>
<%--            <a href="<%=request.getContextPath()%>/dept/detail?f=m&dno=<%=dept.getDeptno()%>">修改</a>--%>
<%--            <a href="<%=request.getContextPath()%>/dept/detail?f=d&dno=<%=dept.getDeptno()%>">詳情</a>--%>
            <%-- 這裡的修改,和詳情都是需要 部門編號,我們可以進行一個統一處理,透過所傳的不同的 f= 值作為
 標記,用於伺服器端的 Servlet 判斷轉發處理到的--%>
            <%--            或者是--%>
            <a href="<%=request.getContextPath()%>/dept/detail?f=edit&dno=<%=dept.getDeptno()%>">修改</a>
            <a href="<%=request.getContextPath()%>/dept/detail?f=detail&dno=<%=dept.getDeptno()%>">詳情</a>
        </td>
    </tr>

    <%
        }
    %>
</table>
<hr>
<a href="<%=request.getContextPath()%>/add.jsp">新增部門</a>
</body>

</html>

當前的oa應用存在的問題:

  • 任何一個使用者都可以訪問這個系統,都可以對這個系統當中的資料進行增刪改這些危險的操作。我只想讓合法的使用者去使用這個系統,不合法的使用者不能訪問這個系統,怎麼辦?

    • 加一個登入功能。登入成功的可以訪問該系統,登入失敗不能訪問。
  • 實現登入功能:

    • 步驟1:資料庫當中新增一個使用者表:t_user
      • t_user表當中儲存的是使用者的登入資訊,最基本的也包括:登入的使用者名稱和登入的密碼。
      • 密碼一般在資料庫表當中儲存的是密文。一般不以明文的形式儲存。(這裡先使用明文方式。)
      • 向t_user表中插入資料。
    • 步驟2:再實現一個登入頁面。
      • 登入頁面上應該有一個登入的表單。有使用者名稱和密碼輸入的框。
      • 使用者點選登入,提交表單,提交使用者名稱和密碼。form是post方式提交。
    • 步驟3:後臺要有一個對應的Servlet來處理登入的請求。
      • 登入成功:跳轉到部門列表頁面。
      • 登入失敗:跳轉到失敗的頁面。
    • 步驟4:再提供一個登入失敗的頁面。
  • 登入功能實現了,目前存在的最大的問題:

    • 這個登入功能目前只是一個擺設,沒有任何作用。只要使用者知道後端的請求路徑,照樣可以在不登入的情況下訪問。
    • 這個登入沒有真正起到攔截的作用。怎麼解決?

解決方式:透過 session 會話機制,判斷是否為同一個使用者,並且登入過。因為涉及到篇幅內容的過多,所以詳細內容大家可以移步至:??? B/S結構系統的會話機制(session)_ChinaRainbowSea的部落格-CSDN部落格

8. 補充:

包名 bean 是什麼意思?

在這裡插入圖片描述

  • javabean(java的logo是一杯冒著熱氣的咖啡。javabean被翻譯為:咖啡豆)
  • java是一杯咖啡,咖啡又是由一粒一粒的咖啡豆研磨而成。
  • 整個java程式中有很多bean的存在。由很多bean組成。
  • 什麼是javabean?實際上javabean你可以理解為符合某種規範的java類,比如:
    • 有無引數構造方法
    • 屬性私有化
    • 對外提供公開的set和get方法
    • 實現java.io.Serializable介面
    • 重寫toString
    • 重寫hashCode+equals
    • ....
  • javabean其實就是java中的實體類。負責資料的封裝。
  • 由於javabean符合javabean規範,具有更強的通用性。

舉例:如下是一個簡單的 bean 類關於 dept 類對應資料庫表當中的 dept 資料表的欄位資訊。

package com.RainbowSea.bean;

import java.io.Serializable;
import java.util.Objects;

public class Dept implements Serializable {

    private static final long serialVersionUID = 1L;

    private String deptno;
    private String dname;
    private String loc;


    public Dept() {
    }

    public Dept(String deptno, String dname, String loc) {
        this.deptno = deptno;
        this.dname = dname;
        this.loc = loc;
    }


    public String getDeptno() {
        return deptno;
    }

    public void setDeptno(String deptno) {
        this.deptno = deptno;
    }

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Dept)) return false;
        Dept dept = (Dept) o;
        return Objects.equals(getDeptno(), dept.getDeptno()) && Objects.equals(getDname(), dept.getDname()) && Objects.equals(getLoc(), dept.getLoc());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getDeptno(), getDname(), getLoc());
    }


    @Override
    public String toString() {
        return "Dept{" +
                "deptno='" + deptno + '\'' +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}


9. 總結:

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

  2. JSP的基礎語法:

    1. 在JSP檔案中直接編寫文字,翻譯的是在 service()方法體內的,翻譯到 servlet類的 service()方法的 out.write("翻譯到這裡") ,直接翻譯到雙引號裡,被java程式當做普通字串列印輸出到瀏覽器。
    2. JSP的註釋:
    <%--JSP的專業註釋,不會被翻譯到java原始碼當中。--%> 不會翻譯對應的資訊。該註解當中的內容會被忽略而不翻譯的。
    
    <!--這種註釋屬於HTML的註釋,這個註釋資訊仍然會被翻譯到java原始碼當中,不建議。-->  翻譯到HTML當中作為 html 的
    註解存在。
    
    1. <% %> 在這個<% java語句; %>符號當中編寫的被視為java程式,被翻譯到Servlet類的 service方法體內部。注意方法體當中不可以編寫什麼內容。同時因為編寫的是 Java程式碼需要遵從 Java的語法。
    <% 
     
    // java語句;
    // 在這裡編寫Java程式,編寫 Java語句。
    // 既然是編寫 Java語句,自然要遵循Java的語法規範。
    // 在這裡編寫的java語句,會被翻譯到 service() 方法體內
    %>
    
    1. <%! %>在這個符號當中編寫的java程式會自動翻譯到service方法之外。簡單的說就是: Servlet 類當中,service() 方法外,那不就是 成員變數,方法,程式碼塊了。
    <%!
    	// 在這個符號當中編寫的java程式會自動翻譯到service方法之外。
        // service() 方法體之外是什麼,自然就是在 Servlet 類裡面了。
    %>
    
    1. <%= %>向瀏覽器前端輸入 Java變數值,而不是變數名。
    <% 
    String name = “jack”; 
    out.write("name = " + name); 
    %>
    
  3. JSP指令的作用:指導JSP的翻譯引擎如何工作(指導當前的JSP翻譯引擎如何翻譯JSP檔案。)主要說明的是 page 指令。指令的格式:

<%@指令名  屬性名=屬性值  屬性名=屬性值  屬性名=屬性值....%>
// 這裡我們只學習 page 指令所以是:
<%@page  屬性名=屬性值  屬性名=屬性值  屬性名=屬性值....%>
  1. JSP 的九大內建物件:所謂的JSP的九大內建物件指的是:在 service() 方法體當中的區域性的物件變數,service() 方法體外是無法使用的。
  2. JSP 第一次訪問速度慢的原因:翻譯為 java,並編譯java,生成 class 檔案
  3. JSP檔案的副檔名必須不是xxx.jsp字尾嗎,不是的,.jsp文`件的副檔名是可以配置的。
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>
  1. 開發JSP的最高境界: 眼前編寫的是JSP程式碼,但是我們心裡,腦袋裡面當中呈現的是 java程式碼。

10. 最後:

限於自身水平,其中存在的錯誤,希望大家給予指教,韓信點兵——多多益善,謝謝大家,江湖再見,後悔有期

在這裡插入圖片描述

相關文章