天天看點

javaEE Web(Tomcat)深度了解 和 Servlet的本質

作者:Java解白

1. 實作一個最基本的web應用(這個web應用中沒有java小程式)

我們首先編寫一個關于靜态的網頁的頁面:一個簡單的 百度連結:如下

<!--index.html檔案-->
<!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>實作一個最基本的web應用(這個web應用中沒有java小程式) </title>
</head>
<body style="text-align:center">
    <h3>百度連結</h3>
    <a href="https://www.baidu.com/">進入百度</a>
</body>
</html>
複制代碼           

具體步驟如下:

第一步: 找到我們安裝 Tomcat 中的路徑找到一個名為 webapps 的目錄。

注意: 我們所有的 web的 app 都要放到該 webapps 目錄下,(沒有為什麼,這是 Tomcat 伺服器的要求,如果不放到這裡,Tomcat 伺服器是無法到你的應用的app,并運作的)。

如下圖所示:

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第二步: 在我們找到的這個 webapps 目錄下建立一個子目錄,名為 Mytest 的目錄。

注意: 這個目錄名 test 就是你這個運作 webapp的名字。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第三步: 将我們上面編寫的 html 靜态檔案,添加到該 Mytest目錄下。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第四步: 啟動我們的 Tomcat 伺服器,打開我們的浏覽器在位址欄上輸入:http://127.0.0.1:8080/項目名/index.html :具體如下:http://127.0.0.1:8080/Mytest/index.html,最後回車,就可以通路到我們編寫的 index.html 這個靜态網頁了。

javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

重點:

我們在浏覽器上直接輸入一個 URL,然後回車,這個操作和點選一個 超連結是本質是一樣的都是靜态通路,既然都是一樣的了。那我們完全可以使用超連結,通路我們在 Tomcat 伺服器上編寫的 index.html 資源檔案了。
<!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>通路我們部署到Tomcat中的index.html資源檔案</title>
</head>
<body>
    <!--注意:我們目前前端上的路徑都以“/”開始的,都是加項目名的。-->
    <a href="http://127.0.0.1:8080/Mytest/index.html">index.html</a>
</body>
</html>
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

在同一個部署到同一個項目下的,資源檔案是可以互相通路的。例如:我們編寫一個名為 index2.html 檔案,通過 index.html 通路 index2.html 資源檔案。

<!--index2.html 檔案-->
<!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>部署到Tomcat伺服器中的index2.html資源檔案</title>
</head>
<body style="text-align:center">
    <h1 >你好世界</h1>
    <!--部署到Tomcat同一個項目下的之間的通路,不需要添加:http://127.0.0.1:8080-->
    <!--注意:我們目前前端上的路徑都以“/”開始的,都是加項目名的。-->
    <a href="/Mytest/index.html">通路我們部署到Tomcat中同一個項目中的的index.html資源檔案</a>
</body>
</html>
複制代碼           

同樣放到我們安裝 Tomcat 伺服器的 webapps 中我們建立的項目 Mytest目錄下

javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

上述這種資源的通路:我們可以稱之為是 靜态資源 。所謂的靜态資源就是,固定的,資料是不會更新的,從一開始就寫死了的。

怎麼能變成動态資源 ,顯然是需要連接配接資料庫的 。

連接配接資料庫需要用 JDBC 程式,也就是需要編寫 Java程式連接配接資料庫,資料庫中右多少條記錄的資訊,頁面會根據資料庫的記錄的資訊,進行一個動态的顯示。這種技術被稱為動态網頁技術 。(動态網頁技術并不是說頁面中有 flash 動畫。動态網頁技術是說頁面中的資料是動态的,是根據資料庫中存儲的資料進行一個時時動态更新變化的 )。這個操作在本部落格的後半部分有詳細說明。

2. 一個動态 Web 頁面中所包含的 “角色” 和 “協定”

如下是一個簡易的 Web 通信圖

javaEE Web(Tomcat)深度了解 和 Servlet的本質

2.1 角色

根據上圖分析:在整個BS結構的系統當中 有哪些人參與進去了

  • 浏覽器軟體的開發團隊 :(浏覽器軟體太多了:谷歌浏覽器,火狐浏覽器,IE 浏覽器...)
  • Web Serve的開發團隊 : Web Server 這個軟體也是太多了:Tomcat,Jetty,WebLogic ,JBOSS,WebSphere...
  • DB Server的開發團隊: DB Server 也是不少的:Oracle,MySQL...
  • Web app的開發團隊: Web 應該程式是我們作為 Java web 程式員 開發的。

2.2 協定

協定: 所謂的協定就是一種規範,大家都要遵守的規範。不過不遵守規範的話。互相之間是無法通信交流的。就不如說:兩個人之間交流:一個人說英語,一個人說中文,這兩個人之間使用的語言不同,也就是遵守的規範不同,兩個人就無法交流了。各自都聽不明白。如果兩個人都遵守同一個規範,都是說中文,或者英文,互相之間就可以明白各自在說什麼了,互相交流了。

同理: 浏覽器前端 與 伺服器後端之間想要互相交流資料資訊,也是要遵守規範。伺服器後端 與 資料庫也是一樣的。

javaEE Web(Tomcat)深度了解 和 Servlet的本質
  • Brower(浏覽器) 與 WebServer 之間有一套傳輸協定:HTTP (超文本傳輸協定)
  • Web Server 與 webapp 之間有一套規範:JavaEE規範比如 Servlet規範 :
  • Webapp 和 DB Server 之間有一套規範:JDBC 規範,關于 JDBC 的詳細内容,大家可以移步至: 初始 JDBC_ChinaRainbowSea的部落格-CSDN部落格
  • Servlet 規範的作用是 : Web Server 和 Webapp 解耦合(讓 Webapp 可以在不同的伺服器上運作,提高代碼的移植性)。
    • Servle 規範包括什麼呢? 規範了哪些接口,規範了哪些類 規範了一個 web 應用中應該有哪些配置檔案。 規範了一個web應用之哦個你配置檔案的名字:web.xml 規範了一個web應用中配置檔案存放的路徑:\webapps\crm\WEB-INF\ 這個目錄下 規範了一個web應用中配置檔案的内容:XML标簽中的類名... 規範了一個合法有效的web應用它的目錄結構應該是怎樣的
  • webapproot |------WEB-INF |------classes(存放位元組碼) |------lib(第三方jar包) |------web.xml(注冊Servlet) |------html |------css |------javascript |------image .... 複制代碼

模拟 Servlet 通信

SUN公司把Server 接口/規範制定出來了

package MyJavaWeb;

/**
 * 我們現在充當的角色是SUN公司。
 * SUN公司把Server 接口/規範制定出來了。
 */
public interface Servlet {
    // 一個專門提供服務的方法
    public abstract void service();
}

複制代碼           

充當的角色為:webapp開發者 三個

package MyJavaWeb;

/**
 * 充當的角色為:webapp開發者
 * 隻要我們webapp開發者的 XXXServlet 都要實作 Servlet 接口
 */
public class BankServlet implements Servlet{
    @Override
    public void service() {
        System.out.println("BankServlet is service...");
    }
}


複制代碼           
package MyJavaWeb;

public class UserLoginServlet implements Servlet{
    @Override
    public void service() {
        System.out.println("UserLoginServlet is Servlet...");
    }
}

複制代碼           
package MyJavaWeb;

public class UserListServlet implements Servlet{
    @Override
    public void service() {
        System.out.println("UserListServlet is Servlet...");
    }
}

複制代碼           

充當Tomcat伺服器的開發者

package MyJavaWeb;


import java.io.FileReader;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Scanner;

/**
 * 充當Tomcat伺服器的開發者
 */
public class Tomcat {
    public static void main(String[] args) throws Exception {
        System.out.println("Tomcat 伺服器啟動成功,開始接收使用者的通路...");

        // 簡單的使用Scanner來模拟一下使用者的請求
        // 使用者通路伺服器是通過浏覽器上的“請求路徑”
        // 也就是說使用者請求路徑不同,背景執行的Servlet 不同
        /*
            /userList   UserListServlet
            /login      UserLoginServlet
            /bank       BankServlet
         */

        System.out.print("請輸入您通路的路徑:");
        Scanner scanner = new Scanner(System.in);

        // 使用者的請求路徑:
        String key = scanner.nextLine();  // Tomcat 伺服器已經擷取到了使用者的請求路徑了

        // Tomcat 伺服器應該通過使用者的請求路徑找到對應的 XXXServlet
        // 請求路徑和XXXServlet 之間的關系應該由誰指定呢? wedapps 的開發者
        // 對應Tomcat 伺服器來說需要解析配置檔案
        //ResourceBundle bundle = ResourceBundle.getBundle("web.properties");

        FileReader reader = new FileReader("servlet01/src/MyJavaWeb/web.properties");
        Properties properties = new Properties();
        properties.load(reader);
        reader.close();

        // 通過 key 擷取value
        String className = properties.getProperty(key);

        // 通過反射機制建立對象,注意:需要無參構造器
        Class clazz = Class.forName(className);
        Object o = clazz.newInstance();

        //因為這裡使用的是多态:所有的 XXXServlet 都實作了其Servlet 接口
        // 但是Tomcat 伺服器的開發者知道: 你寫的XXXServlet 一定實作了 Servlet 接口
        Servlet servlet = (Servlet) o;

        servlet.service();

    }
}

複制代碼           

模拟的 Web.xml配置檔案

javaEE Web(Tomcat)深度了解 和 Servlet的本質
/aaa=MyJavaWeb.BankServlet
/bbb=MyJavaWeb.UserLoginServlet
/ccc=MyJavaWeb.UserListServlet
# 注意不要有空格
複制代碼           

運作

javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

3. 開發一個帶有Servlet(Java小程式)的webapp(重點)

具體步驟如下:

第一步: 在我們的 安裝Tomcat的中的 webapp 目錄下建立一個目錄,起名為 crm (注意:這個crm就是我們的web項目的名稱),當然我們也可以建立其他的項目,比如銀行項目,可以建立一個目錄 bank,辦公系統可以建立一個 oa。等等 注意: crm就是我們這個項目的根了。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第二步: 在我們建立的 crm 目錄下面建立一個名為 WEB-INF 的子目錄。

注意: 這個目錄的名字是被 Servlet 強制規範的,必須全部大寫,必須是 WEB-INF 名,不可以是其他的。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第三步: 在我們剛剛建立的 WEB-INF 目錄下建立一個子目錄:classes

注意: 這個目錄的名字必須是全部小寫的 classes ,名字必須是這個,這也是被 Servlet 強制規範的。另外這個目錄下,存放的是 java程式編譯之後的 class檔案(這裡存放的是位元組碼檔案)

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第四步: 在我們上述建立的 WEB-INF 目錄下建立一個名為 lib 的子目錄。

注意: 這個目錄不是必須要建立的,但如果你的一個 webapp項目需要第三方的 jar包的話,這個 jar 包要放到這個 lib 目錄下,例如:java語言連接配接資料庫需要資料庫的驅動 jar 包,那麼這個jar 包就一定要放到 lib 目錄下。這個目錄的名字也不能随便寫,必須是全部小寫,而且目錄名必須是 lib ,這也是 Servlet 強制規範了的。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第五步: 編寫一個 Java程式 ,這個小Java程式也不能随便開發的,我們開發的這個Java程式必須實作 Servelt 接口。

注意: 這個 Servlet 接口的資訊是不在 JDK 當中的,(因為 Servelt 不是 JavaSE中的内容,Servlet 是屬于 JavaEE的,是另外的一套類庫)。

Servlet 接口(Servlet.class檔案)是由 Oracle 提供的。(最初是 SUN公司提供的)。Servlet 接口是 JavaEE的規範的一員。

Tomcat 伺服器實作了 Servlet 規範,是以 Tomcat 伺服器也需要使用 Servlet 接口。Tomcat 伺服器彙總應該有這個接口。在 Tomcat 伺服器的這樣一個目錄下 apache-tomcat-8.5.82\lib\有這麼一個 servlet-api.jar 檔案,将其解壓這個 servlet-api.jar之後,你會看到有一個 Servlet.class 的位元組碼檔案

javaEE Web(Tomcat)深度了解 和 Servlet的本質

注意: 從 JakartaEE9 開始,Servlet 接口的全名變成了 : jakarta.servlet.Servlet 不是上述 Tomcat 8 這樣的目錄路徑了。

如下是我們編寫的一個 java 小程式。

package com.RainbowSea.servlet;  // 包名

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {

        // 設定一下響應的内容類型是普通文本或Html代碼
        // 需要在擷取流對象之前設定,才有效果
        response.setContentType("text/html");

        System.out.println("My First Servlet, Hello Servlet");  // 在指令行中輸出顯示
        // 怎麼将一個資訊直接輸出到浏覽器上?
        // 需要使用ServletResponse接口 : response
        // response 表示響應:從伺服器向浏覽器發送資料叫響應
        PrintWriter out =  response.getWriter();
        out.print("Hello Servlet, You are My first servlet!"); // 在浏覽器上輸出顯示

        // 這是一個輸出流,負責輸出字元串到浏覽器
        // 這個輸出流不需要我們重新整理,也不需要我們關閉,這些都由Tomcat來維護即可
        /*
        out.flush();
        out.close();
         */

        // 浏覽器是能夠識别 html 代碼的,哪我們是不是應該輸出一段HTML代碼呢?
        out.print("<h1>Hello Servlet<h1> <br>");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

複制代碼           

我們需要将這個Java程式,編譯一下,生成一個 class 的位元組碼檔案,将我們這個Java程式編譯生成的位元組碼檔案,放到我們在 WEB-INF 子目錄下的 classes 目錄下。注意: Java源代碼你原意在哪裡就放在哪裡,這個無所謂,但是你必須把其編譯後生成的class位元組碼檔案存放到 classes 目錄下。這是被 Servlet 規範強制規定的。你隻有将該Java小程式的位元組碼檔案放到 classes 目錄下,Servlet 才能識别到,并運作執行它。

第六步: 怎麼能讓你的 HelloServlet Java小程式編譯通過呢 ?

從上述我們知道了。Servlet 接口的jar包是不在,我們的 JDK 當中的,是以如果我們直接使用指令 javac 編譯的話,會存在一個問題:那就是我們識别出我們這個 HelloServlet Java代碼中的 Servlet 接口,進而導緻編譯無法通過。如下所示

javaEE Web(Tomcat)深度了解 和 Servlet的本質

重點: 你怎麼能讓你的 HelloServleet 編譯通過呢?配置環境變量 ClassPath。

這個 ClassPath 表示的就是類路徑,作用就是當我們通過 cmd 指令執行 javac 編譯的時候,其中存在的類名如果在 JDK中沒有的話,會通過找 我們在 ClassPath環境變量中配置的類路徑去找到對應的 類名中的 class檔案并執行。具體操作如下:

将我們在 Tomcat 中自帶的 servlet-api.jar 包的路徑,配置到我們的 ClassPath 環境變量中就可以了。因為 Tomcat 實作了 Servlet 規範。

javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

最後點選三個确定就配置好了。

配置好以後我們重新 javac 編譯一下這個 HelloServlet Java程式就沒有問題了。(注意:配置好以後,我們需要重新打開一個新的cmd指令視窗,才是使用了我們配置了資訊編譯的)

注意: 使用的 javac 指令

javac -encoding UTF-8 -d . *.java # 編譯該包下所有的java 檔案
# -endoing UTF-8 如果你的代碼中存在中文注釋的話,要附加上這樣一條以 utf-8 編碼,編譯,不然它無法識别你的中文注釋的而導緻編譯無法通過。
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

編譯通過:

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第七步: 将我們上述 HelloServlet 編譯生成的 class 位元組碼檔案,複制粘貼到我們 crm/WEB-INF/classes 目錄下(注意:要連同對應是生成class位元組碼檔案的包名路徑,一起拷貝到 classes 目錄下)

javaEE Web(Tomcat)深度了解 和 Servlet的本質

思考問題:以上配置的CLASSPATH和Tomcat伺服器運作有沒有關系?

沒有任何關系,以上配置這個環境變量隻是為了讓你的HelloServlet能夠正常編譯生成class檔案。

第八步: 在我們上述建立的 WEB-INF 目錄下建立一個檔案:web.xml

javaEE Web(Tomcat)深度了解 和 Servlet的本質

注意: 這個檔案是必須的,這個檔案名必須叫做 web.xml 。這個檔案必須放在 WEB-INF 這個目錄下。一個合法的 webapp, web.xml檔案是必須的,這個 web.xml 檔案就是一個配置檔案,這個配置檔案中描述了請求路徑和 Servlet 類之間的對照關系。

這個檔案最好從其他的 webapp 中拷貝,最好不要手寫,沒有必要,因為容易寫錯,而且寫錯了,還挺難發現的。是以我們複制粘貼就好了。

如下是從其他 webapp 複制過來的通用的 xml 位置檔案内容

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
</web-app>

複制代碼           

在web.xml檔案中編寫配置資訊,讓“請求路徑”和“Servlet類名”關聯在一起。這一步用專業術語描述:在web.xml檔案中注冊Servlet類。

如下是含有 web.xml 的注釋版本的:

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
  
  <!--servlet 描述資訊-->
  
  <servlet>
		<servlet-name>RainbowSea</servlet-name>
        <!--這個位置不能随便寫,必須是該類的全限定類名:帶包名的類名-->
		<servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class>
	</servlet>

    <!--servlet 映射資訊-->
    <!--任何一個servlet都對應這一個servlet-mapping 配置資訊 -->
	<servlet-mapping>
        <!--這個也是随便寫的,但是這裡的名字要和上面的 <servlet-name></servlet-name>兩者之間要保持一緻-->
        <!--一般是和類名一緻-->
		<servlet-name>RainbowSea</servlet-name>
        <!--這裡需要一個路徑:這個路徑唯一的要求就是要 / 開始
        因為我們通過在浏覽器上輸入項目名 + 資源路徑中 url 是要 / 分隔的-->
        <!--這個映射路徑随便寫都可以,但是一定要 “/” 開頭 -->
		<url-pattern>/RainbowSea/Rainbow/Sea</url-pattern>
	</servlet-mapping>
   
</web-app>
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

如下是沒有注釋資訊的 web.xml 資訊,因為如果我們不使用IDEA 內建開發環境編寫的xml 檔案,其中是不能有中文的,有中文是無法識别的

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
  
  <servlet>
		<servlet-name>RainbowSea</servlet-name>
		<servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>RainbowSea</servlet-name>
		<url-pattern>/RainbowSea/Rainbow/Sea</url-pattern>
	</servlet-mapping>

</web-app>
複制代碼           

浏覽器發送請求,到最終伺服器調用 Server 方法中讀取 web.xml 的粗略過程:

  1. 使用者輸入 URL ,或者直接點選超連結:http://127.0.0.1:8080/crm/RainbowSea/Rainbow/Sea 。
  2. 然後 Tomcat 伺服器接收到請求,截取路徑:/crm/RainbowSea/Rainbow/Sea 資訊。
  3. Tomcat 伺服器先找到 webapps 項目下的crm
  4. Tomcat 伺服器在找到我們編寫的配置 web.xml 配置檔案,在該配置檔案中資訊讀取到 <servlet-mapping>: <url-pattern>/RainbowSea/Rainbow/Sea</url-pattern> </servlet-mapping> 在通過這個資訊找到 <servlet-mapping> :<servlet-name>RainbowSea</servlet-name> **</servlet-mapping>**中的 Rainbows 中的資訊。
  5. 找到 <servlet> : <servlet-name>RainbowSea</servlet-name> </servlet> 中的 RainbowSea資訊找到 <servlet> : <servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class> </servlet> 資訊。
  6. 最後我們拿到了 該執行的Java程式的全限定類名:com.RainbowSea.servlet.HelloServlet 。有了該類的全限定類名,我們Tomcat伺服器就可以通過 反射機制 建立 com.RainbowSea.servlet.HelloServlet 的對象了。需要注意的是:如果要使用反射機制的話:該類中必須定義了 無參構造器才行 。因為反射機制的底層是調用了 。該類的無參構造器的。
  7. 最後:Tomcat 伺服器調用 com.RainbowSea.servlet.HelloServlet 對象中的 service 方法。執行程式。

第九步: 啟動 Tomcat 伺服器

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第十步: 打開浏覽器,在浏覽器位址欄上輸入一個 url ,這個 URL 必須是:http://127.0.0.1:8080/項目名/我們在web.xml中編寫的映射路徑,這裡是:http://127.0.0.1:8080/crm/RainbowSea/Rainbow/Sea

  • http://127.0.0.1:8080/ : 表示的是本地計算機上的 Tomcat 伺服器資源
  • crm : 我們上述步驟編寫的 webapps 項目名
  • /RainbowSea/Rainbow/Sea : 我們在 web.xml 中編寫的資源路徑映射的路徑。web.xml檔案中的url-pattern

執行結果:

javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

非常重要的一件事:浏覽器上的請求路徑不能随便寫,這個請求路徑必須和我們編寫的 web.xml檔案中的url-pattern 一緻。

注意: 浏覽器上的請求路徑和 web.xml 檔案中的 url-pattern 的唯一差別就是:浏覽器上的請求路徑帶項目名:/crm

浏覽器上編寫的路徑太複雜,可以使用超連結。注意: 這個我們編寫的 html 頁面隻能放到 WEB-INF 目錄的外面。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

如下是我們編寫的 index.html 資訊

<!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>index page</title>
</head>
<body>
    <a href="/crm/RainbowSea/Rainbow/Sea">hello servlet</a>
</body>
</html>
複制代碼           

執行效果:我們在浏覽器上輸入:http://127.0.0.1:8080/crm/index.html。注意 Tomcat 要保持開啟狀态。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

以後不需要我們編寫 main 方法了。因為 Tomcat 伺服器負責調用 main 方法。Tomcat 伺服器啟動的時候執行的就是 main 方法。我們 JavaWeb 程式員隻需要編寫 Servlet 接口的實作類,然後将其注冊到 web.xml 檔案當中,即可。

3.1 Servlet 連接配接 MySQL8.0 資料庫

上述的 Sevlet 編寫的小程式是靜态的,沒有連接配接資料庫中的資訊。這裡我們來,編寫一個動态的小程式。連接配接了我們的 MySQL 資料庫

第一步: 我們先在資料庫中建立一個名為 dbtest9 的資料庫,在該資料庫下建立一個名為t_student 的表,表中添加一些資料。

SELECT *
FROM emps;

CREATE TABLE t_student (
  `no` INT,
  `name` VARCHAR(255)
);

複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

第二步: 編寫 一個名為 MySQLServlet的ava程式,并編譯生成對應的 class 檔案

package com.RainbowSea.servlet; // 包名

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MySQLServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // 編寫JDBC代碼,連接配接資料庫,查詢所有學生資訊
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 設定一下響應的内容類型是普通文本或Html代碼
        // 需要在擷取流對象之前設定,才有效果
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        try {
            // 1.注冊驅動
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 2. 擷取連接配接
            String url = "jdbc:mysql://localhost:3306/dbtest9";
            String user = "root";
            String password = "MySQL123";

            connection = DriverManager.getConnection(url, user, password);

            // 3. 擷取到預編譯的資料庫連接配接操作對象
            String sql = "select no ,name from t_student";
            preparedStatement = connection.prepareStatement(sql);
            // 4. 執行sql
            resultSet = preparedStatement.executeQuery();
            // 5. 處理查詢結果集
            while (resultSet.next()) {
                String no = resultSet.getString("no");
                String name = resultSet.getString("name");
                out.print(no +"->"+name + "<br>");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 6. 關閉資源

            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }


            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

複制代碼           

同樣使用我們的 cmd 指令編譯生成對應的位元組碼檔案:

javac -encoding UTF-8 -d . *.java # 編譯該包下所有的java 檔案
# -endoing UTF-8 如果你的代碼中存在中文注釋的話,要附加上這樣一條以 utf-8 編碼,編譯,不然它無法識别你的中文注釋的而導緻編譯無法通過。
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

同樣的,根據我們在該Java程式中定義的 : package com.RainbowSea.servlet 生成對應的包(目錄)存放對應的 MySQLServlet.class檔案。如下圖所示:

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第三步 :我們将我們的 MySQLServlet.class 檔案拷貝到,我們webapp項目中的 `

javaEE Web(Tomcat)深度了解 和 Servlet的本質

同樣的:我們需要将我們生成的 class 位元組碼檔案的包括:包名(目錄)完成的拷貝過去。如下

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第四步: 編寫我們在 webapps/crm/WEB-INF 目錄下的 web.xml 檔案。在 web.xml 檔案中編寫配置資訊,讓 “請求路徑” 和 “MySQLServlet類名 ” 關聯在一起。這一步專業術語被稱為是: 在web.xmll 檔案中注冊Servlet類 。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

編寫的web.xml 資訊内容如下: 注意: 如果我們是通過手動的方式編寫的,而沒有借助 IDEA 這樣之類的內建開發環境環境的話。是我們識别我們在 web.xml 中編寫的中文注釋的,編譯無法通過。是以不能有中文。

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
  
  <servlet>
		<servlet-name>RainbowSea</servlet-name>
		<servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>RainbowSea</servlet-name>
		<url-pattern>/RainbowSea/Rainbow/Sea</url-pattern>
	</servlet-mapping>
    
    
      <servlet>
		<servlet-name>MySQLServlet</servlet-name>
		<servlet-class>com.RainbowSea.servlet.MySQLServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>MySQLServlet</servlet-name>
		<url-pattern>/RainbowSea/student/list</url-pattern>
	</servlet-mapping>

</web-app>
複制代碼           

第五步 : 啟動 Tomcat 伺服器。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第六步: 在浏覽器的位址欄上輸入: http://127.0.0.1:8080/crm//RainbowSea/student/list進行通路。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

第七步: 驗證我們的資料資訊是否是:動态更新的:我們更新我們的資料庫表中的資訊。添加一條資料: 7 , Tomcat

javaEE Web(Tomcat)深度了解 和 Servlet的本質
INSERT INTO t_student VALUES(7,"Tomcat");
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質

我們在通路,我們頁面中的資料是否也是同步更新上的。重新整理一下頁面,即可。

javaEE Web(Tomcat)深度了解 和 Servlet的本質

從上述重新整理運作的結果上看,我們的頁面是被同步更新的。

4. 補充:

4.1 JavaEE 的版本說明

JavaEE 目前最高版本是 JavaEE8。

2017 年 8 月,Oracle(甲骨文)決定将 Java EE(Java Enterprise Edition)移交給開源組織,最後 Eclipse 基金會接手。這應該是甲骨文實作對 Java 品牌控制的最新舉措,盡管之前 Sun 的資産已經被甲骨文圍剿得隻剩一個 VirtualBox 還能喘口氣。不過,甲骨文可不允許開源組織用 Java 的名号,于是 Eclipse 選出了 "Jakarta EE" 和"Enterprise Profile"兩個後續按名字,最終前者以 64.4% 的票數獲勝。是以就把 JavaEE 改名了,以後就不叫 JavaEE了,以及就叫做 了 Jakarta EE 。從 JavaEE8 版本更新之後的 JavaEE9 ,就不再是這個 “JavaEE9” 這個名字了,而是叫做了 Jakarta EE9 了。

因為本名都被修改了,是以其中對應的類存在的包名也是被修改了的。具體如下。

注意點:

  • JavaEE8 版本對應的 Servlet 全限定類名是:javax.servlet.Servlet。如下是 Tomcat 8 中 Servlet .java的存放路徑
javaEE Web(Tomcat)深度了解 和 Servlet的本質
  • JakartaEE9 版本的時候對應的 Servlet 全限定類名是:jakarta.servlet.Servlet (包名都被換了)如下是 Tomcat 10 中 Servlet.java的存放路徑。
javaEE Web(Tomcat)深度了解 和 Servlet的本質
  • 如果你之前的項目還是使用 Tomcat 8 中的javax.servlet.Servlet ,那麼你的項目無法直接部署到 Tomcat 10+版本上。你隻能部署到 Tomcat 9- 版本之後上,在 Tomcat 9 以及 Tomcat 9 之前的版本中還是能夠識别 javax.servlet 這個包的。
  • 當然也是有解決方法的。在 Tomcat 的官網上有所說明:tomcat.apache.org/
javaEE Web(Tomcat)深度了解 和 Servlet的本質
javaEE Web(Tomcat)深度了解 和 Servlet的本質

4.2 解決Tomcat伺服器在DOS指令視窗中的亂碼問題(控制台亂碼)

将CATALINA_HOME/conf/logging.properties檔案中的内容修改如下:

java.util.logging.ConsoleHandler.encoding = GBK

具體的可以移步至: Tomcat 的安裝以及其中配置環境原理的詳細說明_tomcat要在什麼環境下運作_ChinaRainbowSea的部落格-CSDN部落格

5. 總結:

  1. 了解一個 web 頁面中所包含的 角色 和 協定:
  2. 角色 : 浏覽器開發團體,Web Serve伺服器開發團隊,webapps 開發團隊,DB資料庫開發團隊。
  3. 協定: 浏覽器頁面 與 web Serve 伺服器之間的協定是 :HTTP 超文本傳輸協定 web Server 伺服器與 webapps 之間的協定是:Java EE 的 中 規範,比如 Servlet 等等等 webapps 與 DB 資料庫之間的協定是:JDBC
  4. Servle 規範包括什麼呢? 規範了哪些接口,規範了哪些類 : 規範了 web 應用中的配置檔案,以及配置檔案名 web.xml,配置檔案的存放路徑:\webapps\crm\WEB-INF\ ,配置檔案的内容:XML 标簽中的類名(在web.xmll 檔案中注冊Servlet類),規範了一個合法有效的 web 應用它的目錄結構應該是怎樣的。
  5. 總結一下:一個合法的webapp目錄結構應該是怎樣的?
webapproot
     |------WEB-INF
     		  |------classes(存放位元組碼)
     		  |------lib(第三方jar包)
     		  |------web.xml(注冊Servlet)
     |------html
     |------css
     |------javascript
     |------image
     ....
複制代碼           
  1. 關于 JavaEE 的版本說明:Servlet 包名的改變所帶來的影響後果:jakartaEE9 版本的時候對應的 Servlet 全限定類名是:jakarta.servlet.Servlet。如果你之前的項目還是使用 Tomcat 8 中的javax.servlet.Servlet ,那麼你的項目無法直接部署到 Tomcat 10+版本上。你隻能部署到 Tomcat 9- 版本之後上,在 Tomcat 9 以及 Tomcat 9 之前的版本中還是能夠識别 javax.servlet 這個包的。 這個問題在 Tomcat 的官網上有已有解決方法。tomcat.apache.org/
  2. 了解web.xml 中編寫的内容資訊。
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                      https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
  version="5.0"
  metadata-complete="true">
  
  <!--servlet 描述資訊-->
  
  <servlet>
		<servlet-name>RainbowSea</servlet-name>
        <!--這個位置不能随便寫,必須是該類的全限定類名:帶包名的類名-->
		<servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class>
	</servlet>

    <!--servlet 映射資訊-->
    <!--任何一個servlet都對應這一個servlet-mapping 配置資訊 -->
	<servlet-mapping>
        <!--這個也是随便寫的,但是這裡的名字要和上面的 <servlet-name></servlet-name>兩者之間要保持一緻-->
        <!--一般是和類名一緻-->
		<servlet-name>RainbowSea</servlet-name>
        <!--這裡需要一個路徑:這個路徑唯一的要求就是要 / 開始
        因為我們通過在浏覽器上輸入項目名 + 資源路徑中 url 是要 / 分隔的-->
        <!--這個映射路徑随便寫都可以,但是一定要 “/” 開頭 -->
		<url-pattern>/RainbowSea/Rainbow/Sea</url-pattern>
	</servlet-mapping>
   
</web-app>
複制代碼           
javaEE Web(Tomcat)深度了解 和 Servlet的本質
  1. 浏覽器發送請求,到伺服器調用 Servlet 中的方法的粗略過程:
    1. 使用者輸入 URL ,或者直接點選超連結:http://127.0.0.1:8080/crm/RainbowSea/Rainbow/Sea 。
    2. 然後 Tomcat 伺服器接收到請求,截取路徑:/crm/RainbowSea/Rainbow/Sea 資訊。
    3. Tomcat 伺服器先找到 webapps 項目下的crm
    4. Tomcat 伺服器在找到我們編寫的配置 web.xml 配置檔案,在該配置檔案中資訊讀取到 <servlet-mapping>: <url-pattern>/RainbowSea/Rainbow/Sea</url-pattern> </servlet-mapping> 在通過這個資訊找到 <servlet-mapping> :<servlet-name>RainbowSea</servlet-name> </servlet-mapping> 中的 Rainbows 中的資訊。
    5. 找到 <servlet> : <servlet-name>RainbowSea</servlet-name> </servlet> 中的 RainbowSea資訊找到 <servlet> : <servlet-class>com.RainbowSea.servlet.HelloServlet</servlet-class> </servlet> 資訊。
    6. 最後我們拿到了 該執行的Java程式的全限定類名:com.RainbowSea.servlet.HelloServlet 。有了該類的全限定類名,我們Tomcat伺服器就可以通過 反射機制 建立 com.RainbowSea.servlet.HelloServlet 的對象了。需要注意的是:如果要使用反射機制的話:該類中必須定義了 無參構造器才行 。因為反射機制的底層是調用了 。該類的無參構造器的。
    7. 最後:Tomcat 伺服器調用 com.RainbowSea.servlet.HelloServlet 對象中的 service 方法。執行程式。

6. 最後:

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