學習JavaWeb這一篇就夠了

2020-10-20 11:00:12

目錄


配套資料,免費下載
連結:https://pan.baidu.com/s/1DNouClLLp4OB8mniUJGmBg
提取碼:dq2w
複製這段內容後開啟百度網路硬碟手機App,操作更方便哦

第一章 開發工具

1.1、JDK安裝

第一步:開啟官網進行下載(https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)

image-20200907214959179

第二步:執行程式進行安裝

全部預設下一步即可,不用管路徑是不是存在中文和空格,無礙!

注意:如果自己會設定JDK,可以更改它的安裝路徑,那相應的環境變數也需要修改!

第三步:系統環境變數設定

此電腦 》 右鍵 》 屬性 》 高階系統設定 》 環境變數 》 系統變數 》 新建(需要新建兩個,然後修改一個)

新建兩個:JAVA_HOME代表Java的安裝目錄、CLASSPATH代表程式執行的時候,優先載入當前目錄中的類,然後再載入指定的類庫

變數名變數值
JAVA_HOMEC:\Program Files\Java\jdk1.8.0_261
CLASSPATH.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;

修改一個:編輯PATH環境變數,它會開啟一個視窗,新添兩條路徑,如下圖所示

  • %JAVA_HOME%\bin
  • %JAVA_HOME%\jre\bin

image-20200907215851494

第四步:測試JDK是否安裝成功

開啟一個cmd命令列視窗,輸入以下兩個命令檢視,如果有內容輸出則證明已經設定成功!

  • java -version
  • javac -version

image-20200907220105432

1.2、Tomcat安裝

第一步:開啟官網進行下載和安裝(http://tomcat.apache.org/)

image-20200907220244579

image-20200907220424149

第二步:系統環境變數設定

此電腦 》 右鍵 》 屬性 》 高階系統設定 》 環境變數 》 系統變數 》 新建(需要新建兩個,然後修改三個)

新建兩個:CATALINA_BASE和CATALINA_HOME均代表Tomcat的安裝目錄

變數名變數值
CATALINA_BASEC:\DevTools\apache-tomcat-8.5.57 (輸入自己解壓的Tomcat目錄,不要照抄我的)
CATALINA_HOMEC:\DevTools\apache-tomcat-8.5.57 (輸入自己解壓的Tomcat目錄,不要照抄我的)

修改一個:編輯CLASSPATH環境變數,它會開啟一個視窗,具體修改,如下所示

  • 未修改前:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
  • 修改以後:.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;%CATALINA_HOME%\lib\servlet-api.jar;

修改兩個:編輯PATH環境變數,它會開啟一個視窗,新添兩條路徑,如下所示

  • %CATALINA_HOME%\bin
  • %CATALINA_HOME%\lib

image-20200907221151092

第三步:測試Tomcat是否安裝成功

開啟一個cmd命令列視窗,輸入以下一個命令檢視,如果有內容輸出則證明已經設定成功!

  • catalina run

image-20200907221408847

  • 開啟瀏覽器,輸入:http://localhost:8080/

image-20200907221604709

如果看到上邊這個介面了,就把剛才開啟的cmd視窗關掉,否則可能會影響後邊IDEA整合Tomcat。

1.3、IDEA安裝

注意:官網的版本會隔一段時間更新一次,我這裡只是告訴大家怎麼下載,不一定要用最新的,我這套課程使用的是IdeaIU-2020.1.2,如果你不是這個版本,那麼我建議你改為這個版本,否則後邊可能會出現一些列問題。

第一步:開啟官網進行下載和安裝(https://www.jetbrains.com/idea/)

image-20200907222300232

image-20200907222332219

第二步:執行程式進行安裝

全部預設下一步即可,如果遇到需要一頁打勾的很多,就把有 64 的那個勾上,它代表在桌面建立一個64位元的快捷方式,沒勾選也沒事,可以在開始選單開啟IDEA!

第三步:關於啟用的問題

由於IDEA是收費軟體,請大家自行購買啟用碼,然後啟用,不啟用也可以試用30天!

第四步:常見的設定頁面

image-20200907223241943

image-20200907223302280

image-20200907223343795

第五步:最終的效果圖

image-20200907223037244

1.4、IDEA整合Tomcat

image-20200907223119686

image-20200907223641318

1.5、IDEA執行JavaWeb

image-20200907223827886

image-20200907223915248

image-20200907224005180

注意:這個彈窗意思是你需不需要每天都讓我提示你一些小技巧,我們選擇關閉,不用搭理他!

image-20200907224035030

注意:如果你有依賴的JAR包,就放到lib資料夾中,然後新增到工程中,方便專案移動的時候,不會丟失JAR包。

至於classes是否需要建立,在這裡我個人認為是不需要建立的,因為編譯器會自動建立,如果你建立了,還必須要修改設定資訊,很麻煩,所以我建議就不要建立了,一般我們也不會建立。

image-20200907224153040

image-20200907224212180

注意:Fix並不是所有電腦都需要點選的,如果它彈出了這個視窗你就點選,沒彈出來就不用管了!

image-20200907224238302

注意:只有上邊點選了Fix才會出來這個頁面,沒有點選Fix,這一步忽略即可,不用糾結!

image-20200907224256452

image-20200907224331553

image-20200909151337789

image-20200909151021071

image-20200909151119004

image-20200909151152023

image-20200909151219563image-20200908180738106

image-20200908180908788

一般瀏覽器也會正常顯示剛才JSP中的檔案內容

image-20200908181054089

image-20200908181130916

第二章 XML(選學)

2.1、XML的概述

XML是可延伸標示語言(eXtensible Markup Language),它被設計用來傳輸和儲存資料,我們一般使用XML檔案來做應用程式的組態檔。

2.2、XML的語法

案例演示:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
	<book category="COOKING">
		<title lang="en">Everyday Italian</title>
		<author>Giada De Laurentiis</author>
		<year>2005</year>
		<price>30.00</price>
	</book>
	<book category="CHILDREN">
		<title lang="en">Harry Potter</title>
		<author>J K. Rowling</author>
		<year>2005</year>
		<price>29.99</price>
	</book>
	<book category="WEB">
		<title lang="en">Learning XML</title>
		<author>Erik T. Ray</author>
		<year>2003</year>
		<price>39.95</price>
	</book>
</bookstore>

樹狀結構:

image-20200826153729304

支援語法:

  • 巢狀標籤
  • 單級標籤
  • 內含屬性

注意:根節點只能有一個

註釋語法:

<!-- comment -->

2.3、XML的解析

解析方式:

  • DOM(Document Object Model):在解析的時候,它會把整個XML檔案讀入記憶體中,形成一個樹狀結構。整個檔案稱之為Document物件,所有元素節點對應Element物件,屬性對應Attribute物件,文字對應Text物件,以上所有物件都可以稱之為Node節點。如果XML特別大,可能會造成記憶體溢位。這種解析方式可以對XML檔案進行增刪改查操作。
  • SAX(Simple API For XML):它是基於事件驅動的一種解析方式,也就是讀取一行,解析一行。在讀取較大的XML檔案的時候,也不會造成記憶體溢位,但是這種解析方式只能進行查詢,不能進行增刪改操作。

案例演示:

工程名稱:XMLDemo

包的名稱:com.caochenlei.xml.parse

依賴檔案:dom4j-1.6.1.jar、jaxen-1.1-beta-6.jar、JUnit 4

測試檔案:bookstore.xml(全路徑:/XMLDemo/bookstore.xml)

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
	<book category="COOKING">
		<title lang="en">Everyday Italian</title>
		<author>Giada De Laurentiis</author>
		<year>2005</year>
		<price>30.00</price>
	</book>
	<book category="CHILDREN">
		<title lang="en">Harry Potter</title>
		<author>J K. Rowling</author>
		<year>2005</year>
		<price>29.99</price>
	</book>
	<book category="WEB">
		<title lang="en">Learning XML</title>
		<author>Erik T. Ray</author>
		<year>2003</year>
		<price>39.95</price>
	</book>
</bookstore>

程式碼名稱:XMLParse.java(全路徑:/XMLDemo/src/com/caochenlei/xml/parse/XMLParse.java)

package com.caochenlei.xml.parse;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.junit.Test;

public class XMLParse {

	/**
	 * 讀取全部
	 */
	@Test
	public void test1() {
		try {
			SAXReader domReader = new SAXReader();
			Document document = domReader.read(new File("bookstore.xml"));
			Element rootElement = document.getRootElement();
			List<Element> bookElements = rootElement.elements("book");
			for (Element bookElement : bookElements) {
				Attribute category = bookElement.attribute("category");
				String title = bookElement.element("title").getText();
				String author = bookElement.element("author").getText();
				String year = bookElement.element("year").getText();
				String price = bookElement.element("price").getText();
				System.out.println(Arrays.asList(category.getText(), title, author, year, price));
			}
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 新增一行
	 */
	@Test
	public void test2() {
		try {
			SAXReader domReader = new SAXReader();
			Document document = domReader.read(new File("bookstore.xml"));
			Element rootElement = document.getRootElement();

			Element bookElement = rootElement.addElement("book");
			bookElement.addAttribute("category", "Hibernate");
			bookElement.addElement("title").addText("Learing Hibernate");
			bookElement.addElement("author").addText("caochenlei");
			bookElement.addElement("year").addText("1997");
			bookElement.addElement("price").addText("99.99");

			XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("bookstore.xml"), OutputFormat.createPrettyPrint());
			xmlWriter.write(document);
			xmlWriter.close();
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 刪除一行
	 */
	@Test
	public void test3() {
		try {
			SAXReader domReader = new SAXReader();
			Document document = domReader.read(new File("bookstore.xml"));
			Element rootElement = document.getRootElement();

			Node singleNode = rootElement.selectSingleNode("//book[@category='Hibernate']");
			rootElement.remove(singleNode);

			XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("bookstore.xml"), OutputFormat.createPrettyPrint());
			xmlWriter.write(document);
			xmlWriter.close();
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 修改一行
	 */
	@Test
	public void test4() {
		try {
			SAXReader domReader = new SAXReader();
			Document document = domReader.read(new File("bookstore.xml"));
			Element rootElement = document.getRootElement();

			Node singleNode = rootElement.selectSingleNode("//book[@category='WEB']");
			singleNode.selectSingleNode("title").setText("Learning JavaWeb");
			singleNode.selectSingleNode("author").setText("張三");
			singleNode.selectSingleNode("year").setText("2020");
			singleNode.selectSingleNode("price").setText("09.09");

			XMLWriter xmlWriter = new XMLWriter(new FileOutputStream("bookstore.xml"), OutputFormat.createPrettyPrint());
			xmlWriter.write(document);
			xmlWriter.close();
		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

第三章 YAML(選學)

3.1、YAML的概述

YAML(YAML Ain’t Markup Language)是一種可讀性高並且容易被人類閱讀,容易和指令碼語言互動,用來表達資料序列的程式語言,類似於XML但比XML更簡潔。

3.2、YAML的語法

YAML語法:

  • 大小寫敏感
  • 使用空格縮排表示層級關係
  • 縮排不允許使用tab,只允許空格
  • 縮排的空格數不重要,只要相同層級的元素左對齊即可
  • '#'表示註釋
  • 相同層級的鍵不能重複

YAML物件:

物件鍵值對使用冒號結構表示 key: value,冒號後面要加一個空格,也可以使用 key:{key1: value1, key2: value2, …},還可以使用縮排表示層級關係。

key: 
  child-key1: value1
  child-key2: value2

較為複雜的物件格式,可以使用問號加一個空格代表一個複雜的 key,配合一個冒號加一個空格代表一個 value:

? 
  - complexkey1
  - complexkey2
: 
  - complexvalue1
  - complexvalue2

意思即物件的屬性是一個陣列 [complexkey1,complexkey2],對應的值也是一個陣列 [complexvalue1,complexvalue2]

YAML陣列:

以 - 開頭的行表示構成一個陣列:

- A
- B
- C

YAML 支援多維陣列,可以使用行內表示:

key: [value1, value2, ...]

資料結構的子成員是一個陣列,則可以在該項下面縮排一個空格:

-
 - A
 - B
 - C

一個相對複雜的例子:意思是 companies 屬性是一個陣列,每一個陣列元素又是由 id、name、price 三個屬性構成。

companies:
 -
  id: 1
  name: company1
  price: 200W
 -
  id: 2
  name: company2
  price: 500W

陣列也可以使用流式(flow)的方式表示:

companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

YAML純量:

純量是最基本的,不可再分的值,包括:

  • 字串
  • 布林值
  • 整數
  • 浮點數
  • Null
  • 時間
  • 日期

使用一個例子來快速瞭解純量的基本使用:

boolean: 
  - TRUE                          #true,True都可以
  - FALSE                         #false,False都可以
float:
  - 3.14                          #可以直接寫浮點小數
  - 6.8523015e+5                  #可以使用科學計數法
int:
  - 123                           #十進位制表示
  - 0b1010_0111_0100_1010_1110    #二進位制表示
null:
  nodeName: 'node'
  parent: ~                       #使用~表示null
string:
  - 哈哈
  - 'Hello World'                 #可以使用雙引號或者單引號包裹特殊字元                 
  - comment1:                     #字串可以拆成多行,每一行會被轉化成一個空格   
      newline1
      newline2
  - comment2: >                   #它會在註釋的最後一行加上\n              
      newline3
      newline4
  - comment3: |                   #它會在註釋的每一行尾加上\n              
      newline3
      newline4
date:
  - 2018-02-17 15:02:31           #日期必須使用ISO 8601格式,即yyyy-MM-dd hh-mm-ss
datetime: 
  - 2018-02-17T15:02:31+08:00     #時間使用ISO 8601格式,時間和日期之間使用T連線,最後使用+代表時區

YAML參照:

& 錨點和 * 別名,可以用來參照:

defaults: &defaults
  adapter: postgres
  host: localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

上邊的程式碼相當於:

defaults:
  adapter: postgres
  host: localhost

development:
  database: myapp_development
  adapter: postgres
  host: localhost

test:
  database: myapp_test
  adapter: postgres
  host: localhost

型別指定:

YAML雖然有型別自動解析,但是有時候我們寫了一個數位,但是我們想讓它是字串型別,就用到了型別指定,只要在值的前邊寫上相對應的型別標識就行了,常見的型別標識有以下幾個:

!!null ''                   # null
!!bool 'yes'                # bool
!!int '3...'                # number
!!float '3.14...'           # number
!!binary '...base64...'     # buffer
!!timestamp 'YYYY-...'      # date
!!omap [ ... ]              # array of key-value pairs
!!pairs [ ... ]             # array or array pairs
!!set { ... }               # array of objects with given keys and null values
!!str '...'                 # string
!!seq [ ... ]               # array
!!map { ... }               # object

3.3、YAML的解析

工程名稱:YAMLDemo

包的名稱:com.caochenlei.yaml.parse

依賴檔案:snakeyaml-1.17.jar、JUnit 4

測試檔案:user.yaml(全路徑:/YAMLDemo/user.yaml)、users.yaml(全路徑:/YAMLDemo/users.yaml)

參考網站:https://bitbucket.org/asomov/snakeyaml/wiki/Home

User.java(全路徑:/YAMLDemo/src/com/caochenlei/yaml/parse/User.java)

package com.caochenlei.yaml.parse;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

public class User {
	private long id;
	private String name;
	private String phone;
	private List<String> hobby;
	private Map<String, BigDecimal> balance;
	private Address address;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	public List<String> getHobby() {
		return hobby;
	}

	public void setHobby(List<String> hobby) {
		this.hobby = hobby;
	}

	public Map<String, BigDecimal> getBalance() {
		return balance;
	}

	public void setBalance(Map<String, BigDecimal> balance) {
		this.balance = balance;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + ", phone=" + phone + ", hobby=" + hobby + ", balance=" + balance + ", address=" + address + "]";
	}
}

Address.java(全路徑:/YAMLDemo/src/com/caochenlei/yaml/parse/Address.java)

package com.caochenlei.yaml.parse;

public class Address {
	private String county;
	private String province;
	private String city;

	public String getCounty() {
		return county;
	}

	public void setCounty(String county) {
		this.county = county;
	}

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public String toString() {
		return "Address [county=" + county + ", province=" + province + ", city=" + city + "]";
	}
}

YAMLDemo.java(全路徑:/YAMLDemo/src/com/caochenlei/yaml/parse/YAMLDemo.java)

package com.caochenlei.yaml.parse;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;
import org.yaml.snakeyaml.Yaml;

public class YAMLDemo {

	/**
	 * 讀取全部:讀取為字串
	 */
	@Test
	public void test1() {
		try {
			Yaml yaml = new Yaml();
			Object user = yaml.load(new FileInputStream(new File("user.yaml")));
			System.out.println(user);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 讀取全部:讀取為物件
	 */
	@Test
	public void test2() {
		try {
			Yaml yaml = new Yaml();
			User user = yaml.loadAs(new FileInputStream(new File("user.yaml")), User.class);
			System.out.println(user);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 讀取全部:讀取為Map集合
	 */
	@Test
	public void test3() {
		try {
			Yaml yaml = new Yaml();
			Map<String, Object> user = (Map<String, Object>) yaml.load(new FileInputStream(new File("user.yaml")));
			System.out.println(user.get("id"));
			System.out.println(user.get("name"));
			System.out.println(user.get("phone"));
			System.out.println(user.get("hobby"));
			System.out.println(user.get("balance"));
			System.out.println(user.get("address"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 讀取全部:讀取多組設定
	 */
	@Test
	public void test4() {
		try {
			Yaml yaml = new Yaml();
			Iterable<Object> users = yaml.loadAll(new FileInputStream(new File("users.yaml")));
			for (Object user : users) {
				System.out.println(user);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 將一個物件持久化到檔案
	 */
	@Test
	public void test5() {
		try {
			Yaml yaml = new Yaml();
			User user = new User();
			user.setId(123456);
			user.setName("王五");
			user.setPhone("15633029014");
			user.setHobby(Arrays.asList("aaa", "bbb", "c"));
			HashMap<String, BigDecimal> balance = new HashMap<String, BigDecimal>();
			balance.put("wechat", new BigDecimal(19.99));
			balance.put("alipay", new BigDecimal(29.99));
			user.setBalance(balance);
			Address address = new Address();
			address.setCity("China");
			address.setProvince("HeBei");
			address.setCity("XingTai");
			user.setAddress(address);
			Writer output = new FileWriter("myuser.yaml");
			yaml.dump(user, output);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 將一個集合持久化到檔案
	 */
	@Test
	public void test6() {
		try {
			Yaml yaml = new Yaml();
			HashMap<String, Object> user = new HashMap<String, Object>();
			user.put("id", 123456);
			user.put("name", "王五");
			user.put("phone", "15633029014");
			user.put("hobby", Arrays.asList("aaa", "bbb", "c"));
			HashMap<String, BigDecimal> balance = new HashMap<String, BigDecimal>();
			balance.put("wechat", new BigDecimal(19.99));
			balance.put("alipay", new BigDecimal(29.99));
			user.put("balance", balance);
			Address address = new Address();
			address.setCity("China");
			address.setProvince("HeBei");
			address.setCity("XingTai");
			user.put("address", address);
			Writer output = new FileWriter("mymap.yaml");
			yaml.dump(user, output);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

第四章 Servlet

4.1、Servlet概述

Java Servlet 是執行在 Web 伺服器或應用伺服器上的程式,它是作為來自 Web 瀏覽器或其他 HTTP 使用者端的請求和 HTTP 伺服器上的資料庫或應用程式之間的中間層。使用 Servlet,您可以收集來自網頁表單的使用者輸入,呈現來自資料庫或者其他源的記錄,還可以動態建立網頁。

4.2、Servlet語法格式

第一步:建立包(com.caochenlei.servlet.demo)

第二步:建立類(com.caochenlei.servlet.demo.MyServlet),並且需要實現Servlet介面中的所有方法

package com.caochenlei.servlet.demo;

import javax.servlet.*;
import java.io.IOException;

public class MyServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("MyServlet init ...");
    }

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

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
            throws ServletException, IOException {
        System.out.println("MyServlet service ...");
    }

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

    @Override
    public void destroy() {
        System.out.println("MyServlet destroy ...");
    }
}

方法介紹:

  • init:Servlet初始化時呼叫的方法
  • getServletConfig:獲取當前Servlet的設定資訊
  • service:呼叫Servlet真正去處理邏輯的方法
  • getServletInfo:它提供有關Servlet的資訊,如作者、版本、版權
  • destroy:Servlet銷燬時呼叫的方法

第三步:設定對映(web.xml中新增以下程式碼)

    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/MyServlet</url-pattern>
    </servlet-mapping>

設定介紹:

servlet標籤用於設定Servlet的基本資訊

  1. servlet-class:代表當前Servlet的具體類路徑,注意最後不包含.java
  2. servlet-name:代表當前Servlet的別名,可以和原Servlet名稱一樣,也可以不一樣,一般我們就一樣就行了

servlet-mapping標籤用於設定請求路徑與具體處理Servlet的對應關係

  1. url-pattern:這裡寫你要匹配的地址路徑
  2. servlet-name:如果匹配到請求,該交給哪一個Servlet處理,這裡的servlet-name其實就是一個Servlet的別名

第四步:啟動應用,然後瀏覽器輸入地址存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyServlet)

image-20200908213033310

第五步:正常關閉Tomcat伺服器,我們會看到呼叫銷燬方法,如下圖所示:

image-20200908214658164

4.3、Servlet執行過程

image-20200908221223043

4.4、Servlet生命週期

Servlet執行在Servlet容器中,其生命週期由容器來管理。

Servlet的生命週期通過javax.servlet.Servlet介面中的init()、service()和destroy()方法來表示。

Servlet的生命週期包含了下面4個階段:

  1. 類載入和範例化
  2. 初始化:init()
  3. 請求處理:service()
  4. 銷燬:destroy()

4.5、Servlet繼承體系

其實我們不難發現,現有的Servlet它的方法比較多,而且大多需要我們自己來實現,那有沒有一種它的實現子類,把大部分方法都是實現了,而我們只要關注請求處理就行了,那答案肯定是有的,這個類就是HttpServlet,我們只要繼承這個類重寫GET、POST方法就能實現一個簡單的Servlet請求處理,Servlet的繼承體系如下圖:

image-20200909134206810

既然我們知道HttpServlet這個類了,我們就要使用一下:

第一步:建立類(com.caochenlei.servlet.demo.MyHttpServlet),並且需要繼承HttpServlet實現doPost、doGet方法。

package com.caochenlei.servlet.demo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyHttpServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        System.out.println("doPost method invoke ...");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        System.out.println("doGet method invoke ...");
    }
}

第二步:設定對映(在web.xml檔案中新增以下程式碼)

    <servlet>
        <servlet-name>MyHttpServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.MyHttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyHttpServlet</servlet-name>
        <url-pattern>/MyHttpServlet</url-pattern>
    </servlet-mapping>

第三步:重新啟動應用,然後瀏覽器存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet),觀察控制檯

注意:doPost需要提交表單模擬,這裡就不演示了,效果都一樣!

image-20200909132323346

我們注意url-pattern它可以有多種攔截形式:

  • 全路徑匹配:/a
  • 前半段匹配:/a/b/c/*
  • 擴充套件名匹配:*.action

4.6、ServletContext

每個web工程都只有一個ServletContext物件,也就是不管在哪個Servlet裡面,獲取到的這個類的物件都是同一個,它用來獲取Servlet的上下文,在伺服器啟動的時候,會為託管的每一個web應用程式,建立一個ServletContext物件,當從伺服器移除託管或者是關閉伺服器時,ServletContext將會被銷燬。它主要有以下幾方面作用:

  1. 獲取全域性設定引數
  2. 獲取web工程中的資源
  3. 在servlet間共用資料域物件

4.6.1、獲取全域性設定引數

第一步:在web.xml中新增以下程式碼

    <context-param>
        <param-name>username</param-name>
        <param-value>zhangsan</param-value>
    </context-param>
    <context-param>
        <param-name>password</param-name>
        <param-value>123456</param-value>
    </context-param>

第二步:在MyHttpServlet的doGet方法中新增以下程式碼

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doGet method invoke ...");

    // 獲取全部初始化名稱和其對應的內容
    Enumeration<String> initParameterNames = getServletContext().getInitParameterNames();
    while(initParameterNames.hasMoreElements()){
        String initParameterName = initParameterNames.nextElement();
        String initParameterValue = getServletContext().getInitParameter(initParameterName);
        System.out.println(initParameterName+":"+initParameterValue);
    }
}

第三步:重新啟動Tomcat伺服器,在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet)

image-20200909140420185

4.6.2、獲取web工程中的資源

第一步:在 myJavaWebDemo 的 web 資料夾中右鍵建立 a.txt 檔案,內容為 Hello,World!

img

第二步:在MyHttpServlet的doGet方法中新增以下程式碼

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doGet method invoke ...");

    // 獲取全部初始化名稱和其對應的內容
    Enumeration<String> initParameterNames = getServletContext().getInitParameterNames();
    while (initParameterNames.hasMoreElements()) {
        String initParameterName = initParameterNames.nextElement();
        String initParameterValue = getServletContext().getInitParameter(initParameterName);
        System.out.println(initParameterName + ":" + initParameterValue);
    }

    // 獲取web工程中的資源的絕對路徑
    String realPath = getServletContext().getRealPath("a.txt");
    System.out.println(realPath);

    // 獲取web工程中的資源的輸入流物件
    InputStream resourceAsStream = getServletContext().getResourceAsStream("a.txt");
    System.out.println(resourceAsStream);
}

第三步:重新啟動Tomcat伺服器,在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet)

注意:當你能拿到一個檔案的絕對路徑或者輸入流以後,就可以對它進行操作了!

image-20200909142517924

4.6.3、在Servlet間共用資料域物件

第一步:建立 LoginServlet 並設定請求對映

LoginServlet

package com.caochenlei.servlet.demo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

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

    }
}

web.xml

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/LoginServlet</url-pattern>
    </servlet-mapping>

第二步:在 LoginServlet的 doGet 中實現登入次數的修改,預設為1

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    // 獲取當前登入次數
    Integer count = (Integer) getServletContext().getAttribute("count");
    // 如果當前登入為null,則設定預設值為1
    if (count == null) {
        getServletContext().setAttribute("count", 1);
    } else {
        getServletContext().setAttribute("count", count + 1);
    }
}

第三步:在 MyHttpServlet 的 doGet 中新增以下檢視程式碼,用於檢視當前登入次數

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("doGet method invoke ...");

    // 獲取全部初始化名稱和其對應的內容
    Enumeration<String> initParameterNames = getServletContext().getInitParameterNames();
    while (initParameterNames.hasMoreElements()) {
        String initParameterName = initParameterNames.nextElement();
        String initParameterValue = getServletContext().getInitParameter(initParameterName);
        System.out.println(initParameterName + ":" + initParameterValue);
    }

    // 獲取web工程中的資源的絕對路徑
    String realPath = getServletContext().getRealPath("a.txt");
    System.out.println(realPath);

    // 獲取web工程中的資源的輸入流物件
    InputStream resourceAsStream = getServletContext().getResourceAsStream("a.txt");
    System.out.println(resourceAsStream);

    // 檢視當前網站登入次數,這個資料是儲存在ServletContext中的
    Integer count = (Integer) getServletContext().getAttribute("count");
    // 判斷是否登入過,如果沒有登入過,提示未登入,如果已經登入過,顯示登入次數
    if (count == null) {
        response.getWriter().write("no login!");
    } else {
        response.getWriter().write("count:" + count);
    }
}

第四步:重新啟動Tomcat伺服器

在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet),檢視是否登入

image-20200909145158131

在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/LoginServlet),模擬登入場景

image-20200909145250386

在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet),檢視登入次數

image-20200909150340563

4.7、ServletConfig

通過ServletConfig物件可以獲取servlet在設定的時候一些資訊。

第一步:建立類(HelloServlet)

package com.caochenlei.servlet.demo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    }

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

    }
}

第二步:在web.xml中設定對映關係

<servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.HelloServlet</servlet-class>
        <!-- Servlet的初始化引數 -->
        <init-param>
            <param-name>driver</param-name>
            <param-value>com.mysql.jdbc.Driver</param-value>
        </init-param>
        <init-param>
            <param-name>url</param-name>
            <param-value>jdbc:mysql://localhost:3306/test</param-value>
        </init-param>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123456</param-value>
        </init-param>
        <!-- Servlet的載入順序 -->
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/HelloServlet</url-pattern>
    </servlet-mapping>

第三步:在HelloServlet的doGet方法中新增以下程式碼

protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    Enumeration<String> initParameterNames = getServletConfig().getInitParameterNames();
    while (initParameterNames.hasMoreElements()) {
        String initParameterName = initParameterNames.nextElement();
        String initParameterValue = getServletConfig().getInitParameter(initParameterName);
        System.out.println(initParameterName + ":" + initParameterValue);
    }
}

第四步:重新啟動Tomcat伺服器,在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/HelloServlet)

image-20200909154053106

4.8、HttpServletRequest

HttpServletRequest這個物件封裝了使用者端提交過來的一切資料。

第一步:修改 index.jsp

<form action="RegisterServlet" method="get">
    賬戶:<input type="text" name="username"><br>
    密碼:<input type="text" name="password"><br>
         <input type="submit" value="註冊">
</form>

第二步:建立類(RegisterServlet)

package com.caochenlei.servlet.demo;

import javafx.print.Collation;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

public class RegisterServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 獲取使用者端傳遞過來的頭部資訊
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            System.out.println(headerName + ":" + headerValue);
        }
        System.out.println("====================");

        // 獲取使用者端傳遞過來的引數資訊
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String parameterName = parameterNames.nextElement();
            String parameterValue = request.getParameter(parameterName);
            // 如果值有多個請使用:request.getParameterValues(parameterName)
            System.out.println(parameterName + ":" + parameterValue);
        }
        System.out.println("====================");

        // 以Map集合的形式獲取使用者端傳遞過來的引數資訊
        Map<String, String[]> parameterMap = request.getParameterMap();
        Set<String> names = parameterMap.keySet();
        for (String name : names) {
            String[] value = parameterMap.get(name);
            System.out.println(name + ":" + Arrays.toString(value));
        }
        System.out.println("====================");

        // 獲取一些其它地址、查詢等資訊
        StringBuffer requestURL = request.getRequestURL();
        String requestURI = request.getRequestURI();
        String servletPath = request.getServletPath();
        String queryString = request.getQueryString();
        System.out.println("requestURL:" + requestURL);
        System.out.println("requestURI:" + requestURI);
        System.out.println("servletPath:" + servletPath);
        System.out.println("queryString:" + queryString);
    }
}

第三步:在web.xml中新增對映資訊

    <servlet>
        <servlet-name>RegisterServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.RegisterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RegisterServlet</servlet-name>
        <url-pattern>/RegisterServlet</url-pattern>
    </servlet-mapping>

第四步:重新啟動Tomcat伺服器,在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/)

在表單輸入資料,然後點選提交

image-20200909174127111

檢視IDEA的控制檯資訊

host:localhost:8080
connection:keep-alive
upgrade-insecure-requests:1
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site:same-origin
sec-fetch-mode:navigate
sec-fetch-user:?1
sec-fetch-dest:document
referer:http://localhost:8080/myJavaWebDemo_war_exploded/
accept-encoding:gzip, deflate, br
accept-language:zh-CN,zh;q=0.9
cookie:JSESSIONID=4342FA7CB5F51C5E4A5251E485E36E38
====================
username:zhangsan
password:123456
====================
username:[zhangsan]
password:[123456]
====================
requestURL:http://localhost:8080/myJavaWebDemo_war_exploded/RegisterServlet
requestURI:/myJavaWebDemo_war_exploded/RegisterServlet
servletPath:/RegisterServlet
queryString:username=zhangsan&password=123456

如何解決請求資料中文亂碼問題?

  • GET方式

    • // 先用原來的編碼解碼再用UTF—8重新編碼。
      String newUsername = new String(username.getBytes("ISO-8859-1"), "UTF-8");
      
  • POST方式

    • // 這行設定一定要寫在getParameter之前。
      request.setCharacterEncoding("UTF-8");
      

4.9、HttpServletResponse

HttpServletResponse這個物件負責返回資料給使用者端。

第一步:建立類(DisplayServlet)

package com.caochenlei.servlet.demo;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DisplayServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        /**
         * 注意:以下兩種方式一次只能使用一種,我們就用字元流方式了,另一種自己演示
         */
        // 以字元流的方式寫資料
        response.getWriter().write("<h1>hello response 111 ...</h1>");

        // 以位元組流的方式寫資料
        // response.getOutputStream().write("hello response 222 ...".getBytes());
    }
}

第二步:在web.xml檔案中新增以下對映資訊

    <servlet>
        <servlet-name>DisplayServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.DisplayServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>DisplayServlet</servlet-name>
        <url-pattern>/DisplayServlet</url-pattern>
    </servlet-mapping>

第三步:重新啟動Tomcat伺服器,在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/DisplayServlet)

image-20200909175839568

如何解決響應資料中文亂碼問題?

  • 以字元流輸出:response.getWriter()

    • response.setCharacterEncoding("UTF-8");
      response.setHeader("Content-Type", "text/html; charset=UTF-8");
      response.getWriter().write("你好,世界!");
      
  • 以位元組流輸出:response.getOutputStream()

    • response.setHeader("Content-Type", "text/html;charset=UTF-8");
      response.getOutputStream().write("你好,世界!".getBytes("UTF-8"));
      

4.10、重定向和請求轉發

  • 重定向
	// 第一種:使用範例
	response.setStatus(302);
	response.setHeader("Location", "login_success.html");*/
		
	// 第二種:使用範例
	response.sendRedirect("login_success.html");

	1. 地址上顯示的是最後的那個資源的路徑地址。

	2. 請求次數最少有兩次,伺服器在第一次請求後,會返回302以及一個地址,瀏覽器在根據這個地址,執行第二次存取。

	3. 可以跳轉到任意路徑,不是自己的工程也可以跳。

	4. 效率稍微低一點,執行兩次請求。 

	5. 後續的請求,沒法使用上一次的request儲存的資料,或者沒法使用上一次的request物件,因為這是兩次不同的請求。
  • 請求轉發
	// 使用範例:
	request.getRequestDispatcher("login_success.html").forward(request, response);

	1. 地址上顯示的是請求servlet的地址,返回200ok。

	2. 請求次數只有一次,因為是伺服器內部幫使用者端執行了後續的工作。 

	3. 只能跳轉自己專案的資源路徑。  

	4. 效率上稍微高一點,因為只執行一次請求。 

	5. 可以使用上一次的request物件。

4.11、Cookie

Cookie其實是一份小資料,它是伺服器給使用者端並且儲存在使用者端上的一份小資料。

第一步:建立類(CookieServlet)

package com.caochenlei.servlet.demo;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CookieServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 獲取Cookie ============================================================
        Cookie[] cookies = request.getCookies();
        if (cookies.length == 1) {
            System.out.println("沒有其它Cookie存在,只有JSESSIONID這個Cookie存在!");

            // 建立Cookie ============================================================
            // 建立Cookie物件
            Cookie cookie = new Cookie("username", "zhangsan");
            // 新增Cookie描述
            cookie.setComment("使用者賬號Cookie");
            // 正值:表示在這個數位過後,cookie將會失效。
            // 負值:關閉瀏覽器,那麼cookie就失效,預設值是 -1。
            // 注意:Cookie是沒有刪除方法的,想讓要Cookie失效或清除,就只要讓當前值為0就可以了。
            cookie.setMaxAge(60 * 60 * 24 * 7);
            // 重新為當前的這個Cookie賦值
            cookie.setValue("lisi");
            // 只有存取域名 localhost 才會帶上該Cookie
            cookie.setDomain("localhost");
            // 只有存取請求 /myJavaWebDemo_war_exploded/CookieServlet 才會帶上該Cookie
            cookie.setPath("/myJavaWebDemo_war_exploded/CookieServlet");
            // 建立Cookie ============================================================

            // 使用響應物件給瀏覽器響應的時候帶上該Cookie
            response.addCookie(cookie);
        } else {
            for (Cookie cookie : cookies) {
                if ("username".equals(cookie.getName())) {
                    System.out.println(cookie.getName() + "====" + cookie.getValue());
                }
            }
        }
        // 獲取Cookie ============================================================
    }
}

第二步:在web.xml中新增對映資訊

    <servlet>
        <servlet-name>CookieServlet</servlet-name>
        <servlet-class>com.caochenlei.servlet.demo.CookieServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CookieServlet</servlet-name>
        <url-pattern>/CookieServlet</url-pattern>
    </servlet-mapping>

第三步:重新啟動Tomcat伺服器

在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/CookieServlet)

image-20200909220703130

在瀏覽器中存取(http://localhost:8080/myJavaWebDemo_war_exploded/DisplayServlet)

image-20200909220826654

4.12、Session

由於Cookie會儲存在使用者端上,所以有安全隱患問題。還有一個問題,Cookie的大小與個數有限制,為了解決這個問題,於是就有了Session,Session是基於Cookie的一種對談機制。Cookie是伺服器返回一小份資料給使用者端,並且存放在使用者端上。Session是資料存放在伺服器端。

常見用法:

// 獲取Session物件
HttpSession session = request.getSession();

// 獲取SessionID
String id = session.getId();

//存值
session.setAttribute(name, value);

//取值
session.getAttribute(name);

//刪值
session.removeAttribute(name);

生命週期:

  • 建立:如果有在servlet裡面呼叫了 request.getSession()
  • 銷燬:關閉伺服器或者session對談時間過期。預設有效期: 30分鐘。

第五章 Listener

Listener是監聽器,監聽Servlet某一個事件的發生或者狀態的改變,它的內部其實就是介面回撥。

5.1、監聽三個作用域建立和銷燬

5.1.1、ServletContextListener

監聽物件:

ServletContextListener用於監聽ServletContext物件作用域建立和銷燬,利用它可以完成自己想要的初始化工作。

生命週期:

servletcontext建立:
	1、啟動伺服器的時候

servletContext銷燬:
	1、關閉伺服器
	2、從伺服器移除專案

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MyContextListener</listener-class>
    </listener>

5.1.2、ServletRequestListener

監聽物件:

ServletRequestListener用於監聽ServletRequest物件作用域建立和銷燬,利用它可以判斷當前受否存在請求。

生命週期:

request建立:
	1、存取伺服器上的任意資源都會有請求出現。
        存取 html		:會
        存取 jsp		:會
        存取 servlet	:會

request銷燬:
	1、伺服器已經對這次請求作出了響應。

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class MyRequestListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("requestInitialized ...");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("requestDestroyed ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MyRequestListener</listener-class>
    </listener>

5.1.3、HttpSessionListener

監聽物件:

HttpSessionListener用於監聽HttpSession物件作用域建立和銷燬,利用它可以統計線上人數。

生命週期:

session的建立:
    1、只要呼叫getSession()方法。
        html	:不會
        jsp		:會
        servlet	:會

session的銷燬:
    1、對談超時30分鐘。
    2、非正常關閉伺服器。
    3、正常關閉伺服器(序列化)

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class MySessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        System.out.println("sessionCreated ...");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("sessionDestroyed ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MySessionListener</listener-class>
    </listener>

5.2、監聽三個作用域屬性狀態變更

5.2.1、ServletContextAttributeListener

主要作用:

監聽ServletContext存值狀態變更。

主要方法:

icon

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

public class MyContextAttributeListener implements ServletContextAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("attributeAdded ...");
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("attributeRemoved ...");
    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("attributeReplaced ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MyContextAttributeListener</listener-class>
    </listener>

5.2.2、ServletRequestAttributeListener

主要作用:

監聽ServletRequest存值狀態變更。

主要方法:

icon

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;

public class MyRequestAttributeListener implements ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("attributeAdded ...");
    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("attributeRemoved ...");
    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("attributeReplaced ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MyRequestAttributeListener</listener-class>
    </listener>

5.2.3、HttpSessionAttributeListener

主要作用:

監聽HttpSession存值狀態變更。

主要方法:

icon

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class MySessionAttributeListener implements HttpSessionAttributeListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("attributeAdded ...");
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("attributeRemoved ...");
    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("attributeReplaced ...");
    }
}

如何設定:

    <listener>
        <listener-class>com.caochenlei.servlet.demo.MySessionAttributeListener</listener-class>
    </listener>

5.3、監聽HttpSession存值狀態變更

5.3.1、HttpSessionBindingListener

主要作用:

監聽物件與session繫結和解除繫結的動作。

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;

public class MySessionBindingListener implements HttpSessionBindingListener {
    @Override
    public void valueBound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("valueBound ...");
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("valueUnbound ...");
    }
}

如何設定:

這一類監聽器不用註冊。

5.3.2、HttpSessionActivationListener

主要作用:

用於監聽現在session的值是鈍化(序列化)還是活化(反序列化)的動作。

鈍化(序列化)  :把記憶體中的資料儲存到硬碟上。
活化(反序列化):把硬碟中的資料讀取到記憶體中。

如何鈍化:

1. 在tomcat的 conf/context.xml 裡面設定
		對所有的執行在這個伺服器的專案生效。

2. 在tomcat的 conf/Catalina/localhost/context.xml 裡面設定
		對localhost生效。

3. 在自己的web工程專案中的 META-INF/context.xml 裡面設定
		只對當前的工程生效。

具體設定資訊如下:
	maxIdleSwap 	: 1分鐘不用就鈍化。
	directory 		: 鈍化後的那個檔案存放的目錄位置。

	<Context>
		<Manager className="org.apache.catalina.session.PersistentManager" maxIdleSwap="1">
			<Store className="org.apache.catalina.session.FileStore" directory="D:/Passivate"/>
		</Manager>
	</Context>

如何建立:

package com.caochenlei.servlet.demo;

import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;

public class MySessionActivationListener implements HttpSessionActivationListener {
    @Override
    public void sessionWillPassivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("sessionWillPassivate ...");
    }

    @Override
    public void sessionDidActivate(HttpSessionEvent httpSessionEvent) {
        System.out.println("sessionDidActivate ...");
    }
}

如何設定:

這一類監聽器不用註冊。

第六章 Filter

6.1、Filter概述

Filter是過濾器,就是對使用者端發出來的請求進行過濾。瀏覽器發出請求,然後伺服器派servlet處理。在中間就可以過濾,其實過濾器起到的是攔截的作用。使用過濾器可以對一些敏感詞彙進行過濾、統一設定編碼、實現自動登入等功能。

6.2、Filter生命週期

  • 建立:在伺服器啟動的時候就建立。
  • 銷燬:在伺服器停止的時候就銷燬。

6.3、Filter語法

如何定義:

package com.caochenlei.servlet.demo;

import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
        System.out.println("MyFilter init ...");
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws ServletException, IOException {
        // 放行,讓請求到達下一個目標。
        chain.doFilter(req, resp);
    }

    public void destroy() {
        System.out.println("MyFilter destroy ...");
    }
}

如何設定:

    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.caochenlei.servlet.demo.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

6.4、Filter執行順序

  1. 使用者端發出請求,先經過過濾器,如果過濾器放行,那麼才能到servlet。
  2. 如果有多個過濾器,那麼他們會按照註冊的對映順序來排隊。只要有一個過濾器不放行,那麼後面排隊的過濾器以及咱們的servlet都不會收到請求。如果全部放行了,那麼回來的時候將會是反向執行,比如以下順序:
filter01 ...
filter02 ...
filter03 ...

filter03 ...
filter02 ...
filter01 ...

注意:init方法的引數 FilterConfig , 可以用於獲取Filter在註冊的名字以及初始化引數,其實這裡的設計的初衷與ServletConfig是一樣的。

6.5、Filter匹配規則

  • 全路徑匹配:/a
  • 前半段匹配:/a/b/c/*
  • 擴充套件名匹配:*.action

6.6、Filter攔截型別

注意:針對 dispatcher 設定的選項。

  • REQUEST : 只要是請求過來都攔截,預設就是REQUEST。
  • FORWARD : 只要是轉發過來都攔截。
  • ERROR : 頁面出錯發生跳轉。
  • INCLUDE : 包含頁面的時候就攔截。

6.7、Filter統一編碼

package com.caochenlei.servlet.demo;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        // 1.強轉
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        // 2.放行
        chain.doFilter(new MyRequest(request), response);
    }

    @Override
    public void destroy() {
    }

}

// 增強了所有的獲取引數的方法request.getParameter("name");
// 增強了所有的獲取引數的方法request.getParameterValues("name");
// 增強了所有的獲取引數的方法request.getParameterMap();
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;
    private boolean flag = true;

    public MyRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    @Override
    public String getParameter(String name) {
        if (name == null || name.trim().length() == 0) {
            return null;
        }
        String[] values = getParameterValues(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        if (name == null || name.trim().length() == 0) {
            return null;
        }
        Map<String, String[]> map = getParameterMap();
        if (map == null || map.size() == 0) {
            return null;
        }
        return map.get(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        String method = request.getMethod();
        if ("post".equalsIgnoreCase(method)) {
            try {
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if ("get".equalsIgnoreCase(method)) {
            Map<String, String[]> map = request.getParameterMap();
            if (flag) {
                for (String key : map.keySet()) {
                    String[] arr = map.get(key);
                    for (int i = 0; i < arr.length; i++) {
                        try {
                            arr[i] = new String(arr[i].getBytes("utf-8"), "utf-8");
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
                flag = false;
            }
            return map;
        }
        return super.getParameterMap();
    }

}
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>com.caochenlei.servlet.demo.EncodingFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

第七章 JSP

7.1、JSP概述

JSP(全稱Java Server Pages)是由 Sun Microsystems 公司倡導和許多公司參與共同建立的一種使軟體開發者可以響應使用者端請求,而動態生成 HTML、XML 或其他格式檔案的Web網頁的技術標準。從使用者角度看待,就是是一個網頁,從程式設計師角度看待,其實是一個Java類,它繼承了Servlet,所以可以直接說JSP就是一個Servlet。

那為什麼會有JSP?

HTML多數情況下用來顯示一成不變的靜態內容,但是有時候我們需要在網頁上顯示一些動態資料,比如:查詢所有的學生資訊、根據姓名去查詢具體某個學生,這些動作都需要去查詢資料庫,然後在網頁上顯示,HTML是不支援寫Java程式碼 ,JSP裡面可以寫Java程式碼。

7.2、JSP生命週期

JSP生命週期就是從建立到銷燬的整個過程,類似於Servlet生命週期,區別在於JSP生命週期還包括將JSP檔案編譯成Servlet。

以下是JSP生命週期中所走過的幾個階段:

  • 編譯階段:Servlet容器編譯Servlet原始檔生成Servlet類。
  • 初始化階段:載入與JSP對應的Servlet類,建立其範例並呼叫它的初始化方法。
  • 執行階段:呼叫與JSP對應的Servlet範例的服務方法。
  • 銷燬階段:呼叫與JSP對應的Servlet範例的銷燬方法銷燬Servlet範例。

7.3、JSP語法

7.3.1、JSP指令碼程式

第一種格式:

格式:

<% Java程式碼片段 %>

範例:

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

第二種格式:

格式:

<jsp:scriptlet>
   Java程式碼片段
</jsp:scriptlet>

範例:

<jsp:scriptlet>
   System.out.println("Hello");
</jsp:scriptlet>

7.3.2、JSP變數宣告

第一種格式:

格式:

<%! 變數宣告 %>

範例:

<%! int a = 10; int b = 20; %>

第二種格式:

格式:

<jsp:declaration>
   變數宣告
</jsp:declaration>

範例:

<jsp:declaration>
   int c = 30;
   int d = 40;
</jsp:declaration>

7.3.3、JSP表示式

一個JSP表示式中包含的指令碼語言表示式,先被轉化成String,然後插入到表示式出現的地方。

由於表示式的值會被轉化成String,所以您可以在一個文字行中使用表示式而不用去管它是否是HTML標籤。

表示式元素中可以包含任何符合Java語言規範的表示式,但是不能使用分號來結束表示式。

第一種格式:

格式:

<%= 表示式 %>

範例:

<%= a %>

第二種格式:

格式:

<jsp:expression>
   表示式
</jsp:expression>

範例:

<jsp:expression>
    a
</jsp:expression>

7.3.4、JSP註釋

不同情況下使用註釋的語法規則:

語法描述
<%-- 註釋 --%>JSP註釋,註釋內容不會被傳送至瀏覽器甚至不會被編譯
HTML註釋,通過瀏覽器檢視網頁原始碼時可以看見註釋內容
<\%代表靜態 <% 常數
%\>代表靜態 %> 常數
\’在屬性中使用的單引號
\"在屬性中使用的雙引號

7.3.5、JSP指令元素

JSP指令用來設定整個JSP頁面相關的屬性,如網頁的編碼方式和指令碼語言。

語法格式:

<%@ 指令 屬性="值" %>

三種指令:

指令描述
<%@ page … %>定義網頁依賴屬性,比如指令碼語言、error頁面、快取需求等等
<%@ include … %>包含其他檔案
<%@ taglib … %>引入標籤庫的定義

7.3.5.1、page指令

page指令為容器提供當前頁面的使用說明,一個JSP頁面可以包含多個page指令。

第一種格式:

<%@ page 屬性="值" %>

第二種格式:

<jsp:directive.page 屬性="值" />

屬性列表:

屬性描述
buffer指定out物件使用緩衝區的大小
autoFlush控制out物件的快取區
contentType指定當前JSP頁面的MIME型別和字元編碼
errorPage指定當JSP頁面發生異常時需要轉向的錯誤處理頁面
isErrorPage指定當前頁面是否可以作為另一個JSP頁面的錯誤處理頁面
extends指定servlet從哪一個類繼承
import匯入要使用的Java類
info定義JSP頁面的描述資訊
isThreadSafe指定對JSP頁面的存取是否為執行緒安全
language定義JSP頁面所用的指令碼語言,預設是Java
session指定JSP頁面是否使用session
isELIgnored指定是否執行EL表示式
isScriptingEnabled確定指令碼元素能否被使用

7.3.5.2、include指令

JSP可以通過include指令來包含其他檔案,被包含的檔案可以是JSP檔案、HTML檔案或文字檔案,包含的檔案就好像是該JSP檔案的一部分,會被同時編譯執行。

第一種格式:

<%@ include file="檔案相對url地址" %>

第二種格式:

<jsp:directive.include file="檔案相對url地址" />

7.3.5.3、taglib指令

JSP允許使用者自定義標籤,一個自定義標籤庫就是自定義標籤的集合,taglib指令引入一個自定義標籤集合的定義,包括庫路徑、自定義標籤。

第一種格式:

<%@ taglib uri="uri" prefix="標籤字首" %>

第二種格式:

<jsp:directive.taglib uri="uri" prefix="標籤字首" />

7.3.6、JSP動作元素

與JSP指令元素不同的是,JSP動作元素在請求處理階段起作用,利用JSP動作可以動態地插入檔案、重用JavaBean元件、把使用者重定向到另外的頁面、為Java外掛生成HTML程式碼。

語法格式:

<jsp:動作名稱 屬性="值" />

常見動作:

語法描述
jsp:include在頁面被請求的時候引入一個檔案。
jsp:useBean尋找或者範例化一個JavaBean。
jsp:setProperty設定JavaBean的屬性。
jsp:getProperty輸出某個JavaBean的屬性。
jsp:forward把請求轉到一個新的頁面。

常見屬性:

語法描述
idid屬性是動作元素的唯一標識,可以在JSP頁面中參照。
動作元素建立的id值可以通過PageContext來呼叫。
scope該屬性用於識別動作元素的生命週期。
id屬性和scope屬性有直接關係,scope屬性定義了相關聯id物件的壽命。
scope屬性有四個可能的值:page、request、session和application。

7.3.6.1、jsp:include動作

jsp:include動作元素用來包含靜態和動態的檔案,該動作把指定檔案插入正在生成的頁面。

語法格式:

注意:前面已經介紹過include指令,它是在JSP檔案被轉換成Servlet的時候引入檔案,而這裡的jsp:include動作不同,插入檔案的時間是在頁面被請求的時候。

<jsp:include page="相對URL地址" flush="true" />

屬性列表:

屬性描述
page包含在頁面中的相對URL地址。
flush布林屬性,定義在包含資源前是否重新整理快取區。

相關範例:

<jsp:include page="myInfo.html" flush="true" />

7.3.6.2、jsp:useBean動作

jsp:useBean動作用來載入一個將在JSP頁面中使用的JavaBean,這個功能非常有用,因為它使得我們可以發揮Java元件複用的優勢。

語法格式:

<jsp:useBean id="ID名稱" class="具體的類" />

屬性列表:

屬性描述
class指定Bean的完整包名。
type指定將參照該物件變數的型別。
beanName通過 java.beans.Beans 的 instantiate() 方法指定Bean的名字。

相關範例:

<jsp:useBean id="user" class="com.caochenlei.servlet.demo.User" />

7.3.6.3、jsp:setProperty動作

jsp:setProperty動作用來設定已經範例化的Bean物件的屬性。

語法格式:

注意:jsp:setProperty只有在新建Bean範例時才會執行,如果是使用現有範例則不執行jsp:setProperty。

第一種格式:

<jsp:useBean id="myName" class="..." />
<jsp:setProperty name="myName" property="屬性名" value="值"/>

第二種格式:

<jsp:useBean id="myName" class="...">
   <jsp:setProperty name="myName" property="屬性名" value="值"/>
</jsp:useBean>

屬性列表:

屬性描述
namename屬性是必需的。它表示要設定屬性的是哪個Bean。
propertyproperty屬性是必需的。它表示要設定哪個屬性。有一個特殊用法:如果property的值是"*",表示所有名字和Bean屬性名字匹配的請求引數都將被傳遞給相應的屬性set方法。
valuevalue 屬性是可選的。該屬性用來指定Bean屬性的值。字串資料會在目標類中通過標準的valueOf方法自動轉換成數位、boolean、Boolean、 byte、Byte、char、Character。例如,boolean和Boolean型別的屬性值(比如"true")通過 Boolean.valueOf 轉換,int和Integer型別的屬性值(比如"42")通過Integer.valueOf轉換。value和param不能同時使用,但可以使用其中任意一個。
paramparam 是可選的。它指定用哪個請求引數作為Bean屬性的值。如果當前請求沒有引數,則什麼事情也不做,系統不會把null傳遞給Bean屬性的set方法。因此,你可以讓Bean自己提供預設屬性值,只有當請求引數明確指定了新值時才修改預設屬性值。

相關範例:

第一種格式:

<jsp:useBean id="user1" class="com.caochenlei.servlet.demo.User" />
<jsp:setProperty name="user1" property="username" value="zhangsan"/>

第二種格式:

<jsp:useBean id="user2" class="com.caochenlei.servlet.demo.User">
    <jsp:setProperty name="user2" property="username" value="lisi"/>
</jsp:useBean>

7.3.6.4、jsp:getProperty動作

jsp:getProperty動作提取指定Bean屬性的值,轉換成字串,然後輸出。

語法格式:

<jsp:getProperty name="myName" property="屬性值" />

屬性列表:

屬性描述
name要檢索的Bean屬性名稱,Bean必須已定義。
property表示要提取Bean屬性的值。

相關範例:

<jsp:getProperty name="user2" property="username" />

7.3.6.5、jsp:forward動作

jsp:forward動作把請求轉到另外的頁面。

語法格式:

<jsp:forward page="相對URL地址" />

屬性列表:

屬性描述
pagepage屬性包含的是一個相對URL。
page的值既可以直接給出,也可以在請求的時候動態計算,可以是一個JSP頁面或者一個 Java Servlet。

相關範例:

<jsp:forward page="myJSP.jsp" />

7.3.7、JSP隱含物件

JSP隱式物件是JSP容器為每個頁面提供的Java物件,開發者可以直接使用它們而不用顯式宣告,JSP隱式物件也被稱為預定義變數。

JSP所支援的九大隱式物件:

物件描述
requestHttpServletRequest 介面的範例
responseHttpServletResponse 介面的範例
sessionHttpSession 類的範例
applicationServletContext 類的範例,與應用上下文有關
configServletConfig 類的範例
outJspWriter 類的範例,用於把結果輸出至網頁上
pageContextPageContext 類的範例,提供對JSP頁面所有物件以及名稱空間的存取
page類似於Java類中的 this 關鍵字
ExceptionException 類的物件,代表發生錯誤的JSP頁面中對應的異常物件

JSP所支援的四大作用域:

  • pageContext 【PageContext】

作用域僅限於當前的頁面,還可以獲取到其他八個內建物件。

  • request 【HttpServletRequest】

作用域僅限於一次請求, 只要伺服器對該請求做出了響應,這個域中存的值就沒有了。

  • session 【HttpSession】

作用域限於一次對談(多次請求與響應) 當中。

  • application 【ServletContext】

整個工程都可以存取,伺服器關閉後就不能存取了

7.3.8、JSP常見控制

if語句:

<%! int age = 19; %>
<% if ( age > 18 ) { %>
    <p>已成年</p><br />
<% } else { %>
    <p>未成年</p><br />
<% } %>

for語句:

<%--for語句--%>
<%! int fontSize1 = 1; %>
<% for (fontSize1 = 1; fontSize1 <= 3; fontSize1++){ %>
    <font color="green" size="<%= fontSize1 %>">fontSize1</font><br />
<% } %>

7.4、EL表示式

7.4.1、EL概述

EL是為了簡化咱們的jsp程式碼,具體一點就是為了簡化在jsp裡面寫的那些java程式碼。

7.4.2、EL語法

注意:如果從作用域中取值,會先從小的作用域開始取,如果沒有,就往下一個作用域取,一直把四個作用域取完都沒有,就沒有顯示。

${ 表示式 }

7.4.3、EL隱含物件

EL表示式的11個內建物件:

隱含物件描述
pageScopepage 作用域
requestScoperequest 作用域
sessionScopesession 作用域
applicationScopeapplication 作用域
paramRequest 物件的引數,字串
paramValuesRequest 物件的引數,字串集合
headerHTTP 資訊頭,字串
headerValuesHTTP 資訊頭,字串集合
initParam上下文初始化引數
cookieCookie值
pageContext當前頁面的pageContext

注意:您可以在表示式中使用這些物件,就像使用變數一樣。

7.4.4、EL案例演示

如果域中所存的是物件:

<%
    User u = new User();
    u.setUsername("zhangsan");
    u.setPassword("123456");

    pageContext.setAttribute("u", u);
    request.setAttribute("u", u);
    session.setAttribute("u", u);
    application.setAttribute("u", u);
%>

<br>使用普通手段取出作用域中的值<br>

<%= ((User) pageContext.getAttribute("u")).getUsername() %>
<%= ((User) request.getAttribute("u")).getUsername() %>
<%= ((User) session.getAttribute("u")).getUsername() %>
<%= ((User) application.getAttribute("u")).getUsername() %>

<br>使用EL表示式取出作用域中的值<br>

<p>${ pageScope.u.username }</p>
<p>${ requestScope.u.username }</p>
<p>${ sessionScope.u.username }</p>
<p>${ applicationScope.u.username }</p>

如果域中所存的是鍵值:

<%
    pageContext.setAttribute("name", "page");
    request.setAttribute("name", "request");
    session.setAttribute("name", "session");
    application.setAttribute("name", "application");
%>

<br>使用普通手段取出作用域中的值<br>

<%= pageContext.getAttribute("name") %>
<%= request.getAttribute("name") %>
<%= session.getAttribute("name") %>
<%= application.getAttribute("name") %>

<br>使用EL表示式取出作用域中的值<br>

${ pageScope.name }
${ requestScope.name }
${ sessionScope.name }
${ applicationScope.name }	

如果域中所存的是陣列:

<%
    String[] array = {"aa","bb","cc","dd"};

    pageContext.setAttribute("array", array);
    request.setAttribute("array", array);
    session.setAttribute("array", array);
    application.setAttribute("array", array);
%>

<br>使用普通手段取出作用域中的值<br>

<p><%= ((String[]) pageContext.getAttribute("array"))[0] %></p>
<p><%= ((String[]) request.getAttribute("array"))[0] %></p>
<p><%= ((String[]) session.getAttribute("array"))[0] %></p>
<p><%= ((String[]) application.getAttribute("array"))[0] %></p>

<br>使用EL表示式取出作用域中的值<br>

<p>${ pageScope.array[0] }</p>
<p>${ requestScope.array[0] }</p>
<p>${ sessionScope.array[0] }</p>
<p>${ applicationScope.array[0] }</p>

如果域中鎖存的是集合:

<%
    Map map = new HashMap();
    map.put("name", "zhangsan");
    map.put("age",18);

    pageContext.setAttribute("map", map);
    request.setAttribute("map", map);
    session.setAttribute("map", map);
    application.setAttribute("map", map);
%>

<br>使用普通手段取出作用域中的值<br>

<p><%= ((Map) pageContext.getAttribute("map")).get("name") %></p>
<p><%= ((Map) request.getAttribute("map")).get("name") %></p>
<p><%= ((Map) session.getAttribute("map")).get("name") %></p>
<p><%= ((Map) application.getAttribute("map")).get("name") %></p>

<br>使用EL表示式取出作用域中的值<br>

<p>${ pageScope.map.name }</p>
<p>${ requestScope.map.name }</p>
<p>${ sessionScope.map['name'] }</p>
<p>${ applicationScope.map['name'] }</p>

7.5、JSTL表示式

7.5.1、JSTL概述

JSTL(JSP Standard Tag Library,標準標籤庫)主要也是為了簡化jsp的程式碼編寫,替換 <%%> 寫法,一般與EL表示式配合使用。

7.5.2、JSTL依賴

  1. 匯入依賴庫:jstl.jar、standard.jar
  2. 引入標籤庫:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

7.5.3、JSTL常用標籤

  • c:set
<!-- 宣告一個物件 myname,物件的值 zhangsan,儲存到了 page(預設),指定是 session 域 -->
<c:set var="myname" value="zhangsan" scope="session"></c:set>
${ sessionScope.myname }
  • c:if
<c:set var="age" value="18" ></c:set>
<c:if test="${ age > 26 }">
    年齡大於了26歲...
</c:if>
<c:if test="${ age <= 26 }">
    年齡小於了26歲...
</c:if>

<%--定義一個變數名 flag,去接收前面表示式的值,然後存在 session 域中--%>
<c:if test="${ age > 26 }" var="flag" scope="session">
    年齡大於了26歲...
</c:if>
  • c:forEach
<%--從1開始遍歷到10,得到的結果,賦值給 i,並且會儲存到page域中,step代表增幅為2--%>
<c:forEach begin="1" end="10" var="i" step="2">
    ${i}
</c:forEach>

<%
    List<User> list = new ArrayList<User>();

    User u1 = new User();
    u1.setUsername("zhangsan");
    u1.setPassword("123456");
    list.add(u1);

    User u2 = new User();
    u2.setUsername("lisi");
    u2.setPassword("123456");
    list.add(u2);

    request.setAttribute("list",list);
%>
<!--items:表示遍歷哪一個物件,注意這裡必須寫EL表示式。
	var:遍歷出來的每一個元素用user去接收。 -->
<c:forEach var="user" items="${ list }">
    ${ user.username } ---- ${ user.password } <br />
</c:forEach>

第八章 Servlet3.0

Servlet 3.0 作為 Java EE 6 規範體系中一員,隨著 Java EE 6 規範一起釋出。該版本在前一版本(Servlet 2.5)的基礎上提供了若干新特性用於簡化 Web 應用的開發和部署。其中有幾項特性的引入讓開發者感到非常興奮,同時也獲得了 Java 社群的一片讚譽之聲:

  1. 新增的註解支援:該版本新增了若干註解,用於簡化 Servlet、過濾器(Filter)和監聽器(Listener)的宣告,這使得 web.xml 部署描述檔案從該版本開始不再是必選的了。
  2. 檔案上傳API簡化:從該版本開始,極大地簡化了檔案上傳的操作。
  3. 非同步處理支援:有了該特性,Servlet 執行緒不再需要一直阻塞,直到業務處理完畢才能再輸出響應,最後才結束該 Servlet 執行緒。在接收到請求之後,Servlet 執行緒可以將耗時的操作委派給另一個執行緒來完成,自己在不生成響應的情況下返回至容器。針對業務處理較耗時的情況,這將大大減少伺服器資源的佔用,並且提高並行處理速度。
  4. 動態註冊元件:在初始化ServletContext容器的時候,可以支援動態註冊三大元件。
  5. 可插性支援:如果說 3.0 版本新增的註解支援是為了簡化 Servlet/ 過濾器 / 監聽器的宣告,從而使得 web.xml 變為可選設定, 那麼新增的可插性 (pluggability) 支援則將 Servlet 設定的靈活性提升到了新的高度。熟悉 Struts2 的開發者都知道,Struts2 通過外掛的形式提供了對包括 Spring 在內的各種開發框架的支援,開發者甚至可以自己為 Struts2 開發外掛,而 Servlet 的可插性支援正是基於這樣的理念而產生的。使用該特性,現在我們可以在不修改已有 Web 應用的前提下,只需將按照一定格式打成的 JAR 包放到 WEB-INF/lib 目錄下,即可實現新功能的擴充,不需要額外的設定。

8.1、註解開發

8.1.1、servlet註解

如何建立:

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(
        name = "AnnotationServlet",//代表servlet名稱
        value = {"/AnnotationServlet"},//代表servlet對映地址,可以寫多個,value與urlPatterns一樣,二者不能同時出現
        loadOnStartup = 2,//代表servlet初始化順序
        initParams = {@WebInitParam(name = "user",value = "zhangsan")},//代表servlet初始化引數,可以寫多個
        asyncSupported = false//代表servlet是否支援非同步,預設為false
)
public class AnnotationServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("doPost ...");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("doGet ...");
    }
}

屬性列表:

屬性名型別描述
nameString指定 Servlet 的 name 屬性,如果沒有顯式指定,則該 Servlet 的取值即為類的全限定名
valueString[]該屬性等價於 urlPatterns 屬性,兩個屬性不能同時使用
urlPatternsString[]指定一組 Servlet 的 URL 匹配模式
loadOnStartupint指定 Servlet 的載入順序
initParamsWebInitParam[]指定一組 Servlet 初始化引數
asyncSupportedboolean宣告 Servlet 是否支援非同步操作模式
descriptionString該 Servlet 的描述資訊
displayNameString該 Servlet 的顯示名,通常配合工具使用

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/AnnotationServlet

檢測控制檯輸出:

image-20200912214143931

8.1.2、filter註解

如何建立:

package com.caochenlei.servlet3.annotation;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;

@WebFilter(
        filterName = "AnnotationFilter",//代表filter名稱
        value = {"/*"},//代表filter對映路徑,可以寫多個,value與urlPatterns一樣,二者不能同時出現
        dispatcherTypes = {DispatcherType.REQUEST},//代表filter攔截型別
        initParams = {@WebInitParam(name = "user", value = "zhansan")},//代表filter初始化引數,可以寫多個
        asyncSupported = false,//代表filter是否支援非同步,預設為false
        servletNames = {"AnnotationServlet"}//代表filter指定攔截哪幾個servlet
)
public class AnnotationFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws ServletException, IOException {
        System.out.println("doFilter ...");
        chain.doFilter(req, resp);
    }

    public void destroy() {
    }
}

屬性列表:

屬性名型別描述
filterNameString指定過濾器的 name 屬性
valueString[]該屬性等價於 urlPatterns 屬性,但是兩者不應該同時使用
urlPatternsString[]指定一組過濾器的 URL 匹配模式
servletNamesString[]指定過濾器將應用於哪些 Servlet
dispatcherTypesDispatcherType指定過濾器的轉發模式
具體取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST
initParamsWebInitParam[]指定一組過濾器初始化引數
asyncSupportedboolean宣告過濾器是否支援非同步操作模式
descriptionString該過濾器的描述資訊
displayNameString該過濾器的顯示名,通常配合工具使用

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/AnnotationServlet

檢測控制檯輸出:

image-20200912215136141

8.1.3、listener註解

如何建立:

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener()
public class AnnotationListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ...");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");
    }
}

如何測試:

重新啟動伺服器,觀察控制檯:

image-20200912215910354

image-20200912215930667

8.1.4、兩種設定同時存在

  • 對於servlet來說:
    • 若兩種設定方式的url-pattern值相同,則應用無法啟動。
    • 若兩種設定方式的url-pattern值相同,那麼相當該servlet具有兩個對映url-pattern。
  • 對於filter來說:
    • 無論兩種設定方式的url-pattern值是否相同,其都是作為獨立的filter出現的。
  • 對於listener來說:
    • 如果兩種設定方式都進行了同一個listener註冊,那麼也只能算一個listener。

8.1.5、如何禁用註解元件

如果只想要使用web.xml中的設定而忽略註解註冊的元件,只需要在web.xml跟標籤新增一個屬性即可。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0" metadata-complete="false">
</web-app>

metadata-complete="false":表示web.xml設定和註解設定同時生效,預設是false。

metadata-complete="true":表示web.xml設定有效,而註解設定則被忽略。

8.2、檔案上傳

前臺頁面:

  <form action="uploadServlet" method="post" enctype="multipart/form-data">
    選擇檔案:<input type="file" name="myfile" /> <br />
    上傳檔案:<input type="submit" value="上傳" />
  </form>

上傳模組:

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;

@WebServlet("/uploadServlet")
@MultipartConfig
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        Part part = request.getPart("myfile");
        part.write("D:/xxx.txt");
        response.setHeader("Content-Type", "text/html;charset=UTF-8");
        response.getWriter().println("檔案上傳成功!");

    }
}

@MultipartConfig註解:

屬性名型別是否可選描述
fileSizeThresholdint當資料量大於該值時,內容將被寫入檔案
locationString存放生成的檔案地址
maxFileSizelong允許上傳的檔案最大值。預設值為-1,表示沒有限制
maxRequestSizelong針對該multipart/form-data請求的最大數量,預設值為-1,表示沒有限制

如何測試:

image-20200912222942202

image-20200912222913813

image-20200912223106718

8.3、非同步處理

注意問題:

如果你的servlet開啟了非同步支援,那麼你的filter也必須開啟非同步支援,否則會報錯!

經典場景:

在使用者註冊的時候,通常會傳送一封註冊通知郵件,這裡就是用到了非同步處理的技術。

如何實現:

第一步:修改AnnotationFilter的asyncSupported 為 true

第二步:建立非同步支援的註冊servlet

package com.caochenlei.servlet3.annotation;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet(value = "/RegisterServlet",asyncSupported = true)
public class RegisterServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 獲取非同步支援上下文
        AsyncContext asyncContext = request.startAsync();
        // 啟動一個耗時子執行緒
        EmailSendThread est = new EmailSendThread(asyncContext);
        // 設定非同步超時的時間
        asyncContext.setTimeout(3000);
        // 開啟非同步上下文物件
        asyncContext.start(est);

        // 模擬註冊成功的提示
        response.setHeader("Content-Type", "text/html;charset=UTF-8");
        response.getWriter().println("恭喜您註冊成功,請檢查您的郵箱進行啟用!");
    }
}

第三步:建立傳送郵件的子程序類

package com.caochenlei.servlet3.annotation;

import javax.servlet.AsyncContext;

public class EmailSendThread implements Runnable {
    private AsyncContext ac;

    public EmailSendThread(AsyncContext ac) {
        this.ac = ac;
    }

    public AsyncContext getAc() {
        return ac;
    }

    public void setAc(AsyncContext ac) {
        this.ac = ac;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("確認郵件已經傳送,請及時查收!");
    }
}

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/RegisterServlet

檢測控制檯輸出:

三秒後頁面輸出:

image-20200912225334502

十秒後控制檯輸出:

image-20200912225356682

8.4、動態註冊

動態註冊就是在tomcat啟動的時候,利用ServletContext進行元件動態註冊的技術。

修改AnnotationListener,以後三大組建的設定都是在contextInitialized方法中進行。

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebListener;

@WebListener()
public class AnnotationListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ...");
        ServletContext servletContext = servletContextEvent.getServletContext();
        
        // 三大元件註冊程式碼位置 ...
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");
    }
}

8.4.1、servlet動態註冊

建立一個普通的servlet:

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class NormalServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("NormalServlet doPost ...");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("NormalServlet doGet ...");
    }
}

利用ServletContextListener進行動態註冊:

// 動態註冊servlet
String servletName = "NormalServlet";
String servletClass = "com.caochenlei.servlet3.annotation.NormalServlet";
ServletRegistration.Dynamic srd = servletContext.addServlet(servletName, servletClass);
srd.addMapping("/NormalServlet");

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/NormalServlet

檢測控制檯輸出:

image-20200912230733138

8.4.2、filter動態註冊

建立一個普通的filter:

package com.caochenlei.servlet3.annotation;

import javax.servlet.*;
import java.io.IOException;

public class NormalFilter implements Filter {
    public void init(FilterConfig config) throws ServletException {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws ServletException, IOException {
        chain.doFilter(req, resp);
        System.out.println("NormalFilter doFilter ...");
    }

    public void destroy() {
    }
}

利用ServletContextListener進行動態註冊:

注意:addMappingForServletNames的第二個引數為true代表,在以前的過濾之後過濾,為false,代表在以前的過濾之前過濾。

// 動態註冊filter
String filterName = "NormalFilter";
String filterClass = "com.caochenlei.servlet3.annotation.NormalFilter";
FilterRegistration.Dynamic frd = servletContext.addFilter(filterName, filterClass);
frd.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/a");

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/a

檢測控制檯輸出:

image-20200912231942921

8.4.3、listener動態註冊

建立一個普通的listener:

package com.caochenlei.servlet3.annotation;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

public class NormalListener implements ServletRequestListener {
    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        System.out.println("NormalListener requestInitialized ...");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("NormalListener requestDestroyed ...");
    }
}

利用ServletContextListener進行動態註冊:

// 動態註冊listener
servletContext.addListener("com.caochenlei.servlet3.annotation.NormalListener");

如何測試:

開啟瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/index.jsp

檢測控制檯輸出:

image-20200912232247479

8.5、可插性支援

如何實現模組化開發?

  • 編寫一個類繼承自 HttpServlet,並且在該類上使用 @WebServlet 註解將該類宣告為 Servlet,將該類放在 classes 目錄下的對應包結構中,無需修改 web.xml 檔案。

  • 編寫一個類繼承自 HttpServlet,將該類打成 JAR 包,並且在 JAR 包的 META-INF 目錄下放置一個 web-fragment.xml 檔案,該檔案中宣告了相應的 Servlet 設定。web-fragment.xml 檔案範例如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">

</web-fragment>

從上面的範例可以看出,web-fragment.xml 與 web.xml 除了在頭部宣告的 XSD 參照不同之外,其主體設定與 web.xml 是完全一致的。

由於一個 Web 應用中可以出現多個 web-fragment.xml 宣告檔案,加上一個 web.xml 檔案,載入順序問題便成了不得不面對的問題。Servlet 規範的專家組在設計的時候已經考慮到了這個問題,並定義了載入順序的規則。

web-fragment.xml 包含了兩個可選的頂層標籤, 如果希望為當前的檔案指定明確的載入順序,通常需要使用這兩個標籤, 主要用於標識當前的檔案,而 則用於指定先後順序。一個簡單的範例如下:

<web-fragment...>
    <name>FragmentA</name>
    <ordering>
        <after>
            <name>FragmentB</name>
            <name>FragmentC</name>
        </after>
        <before>
            <others/>
        </before>
    </ordering>
    ...
</web-fragment>

接下來我們使用一個完整範例來演示:

我們先看下我們目前已經做了哪些工程:

image-20200913075756862

建立一個JavaWeb片段工程,可以算是一個功能模組,具體步驟如下圖:

image-20200913075848749

image-20200913075922078

image-20200913075959981

image-20200913080103129

image-20200913080129954

image-20200913080252409

image-20200913080336915

image-20200913080359392

image-20200913080434432

把以下程式碼拷貝到組態檔中:

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd">

</web-fragment>

image-20200913080542561

建立一個需要測試的Servlet,這裡我們直接採用註解開發

image-20200913080756126

image-20200913080829311

image-20200913080956887

接下來我們需要把當前這個工程編譯為一個jar包,以方便嵌入到別的工程中

image-20200913081106654

image-20200913081149935

image-20200913081217309

image-20200913081232746

image-20200913081325671

image-20200913081400378

編譯完成,就會出現這個jar包,把它拷貝到桌面,備用,然後關閉當前工程

image-20200913081529575

image-20200913081611871

開啟之前的servlet3.0的專案,我們把剛才編譯好的jar包放到這個工程中,用來測試是不是可行

image-20200913081710460

image-20200913081754969

image-20200913081947108

image-20200913082011629

image-20200913082152803

image-20200913082223988

image-20200913082245084

image-20200913082302433

把剛才複製到桌子上的fragment.jar複製到lib中

image-20200913082343006

啟動伺服器,然後輸入FragmentServlet的對映網址,看看控制檯會不會輸出

image-20200913082602992

開啟瀏覽器,輸入:http://localhost:8080/servlet3_0_war_exploded/FragmentServlet

image-20200913082700341