目錄
- 第一章 開發工具
- 1.1、JDK安裝
- 1.2、Tomcat安裝
- 1.3、IDEA安裝
- 1.4、IDEA集成Tomcat
- 1.5、IDEA運行JavaWeb
- 第二章 XML(選學)
- 2.1、XML的概述
- 2.2、XML的語法
- 2.3、XML的決議
- 第三章 YAML(選學)
- 3.1、YAML的概述
- 3.2、YAML的語法
- 3.3、YAML的決議
- 第四章 Servlet
- 4.1、Servlet概述
- 4.2、Servlet語法格式
- 4.3、Servlet執行程序
- 4.4、Servlet生命周期
- 4.5、Servlet繼承體系
- 4.6、ServletContext
- 4.6.1、獲取全域配置引數
- 4.6.2、獲取web工程中的資源
- 4.6.3、在Servlet間共享資料域物件
- 4.7、ServletConfig
- 4.8、HttpServletRequest
- 4.9、HttpServletResponse
- 4.10、重定向和請求轉發
- 4.11、Cookie
- 4.12、Session
- 第五章 Listener
- 5.1、監聽三個作用域創建和銷毀
- 5.1.1、ServletContextListener
- 5.1.2、ServletRequestListener
- 5.1.3、HttpSessionListener
- 5.2、監聽三個作用域屬性狀態變更
- 5.2.1、ServletContextAttributeListener
- 5.2.2、ServletRequestAttributeListener
- 5.2.3、HttpSessionAttributeListener
- 5.3、監聽HttpSession存值狀態變更
- 5.3.1、HttpSessionBindingListener
- 5.3.2、HttpSessionActivationListener
- 第六章 Filter
- 6.1、Filter概述
- 6.2、Filter生命周期
- 6.3、Filter語法
- 6.4、Filter執行順序
- 6.5、Filter匹配規則
- 6.6、Filter攔截型別
- 6.7、Filter統一編碼
- 第七章 JSP
- 7.1、JSP概述
- 7.2、JSP生命周期
- 7.3、JSP語法
- 7.3.1、JSP腳本程式
- 7.3.2、JSP變數宣告
- 7.3.3、JSP運算式
- 7.3.4、JSP注釋
- 7.3.5、JSP指令元素
- 7.3.5.1、page指令
- 7.3.5.2、include指令
- 7.3.5.3、taglib指令
- 7.3.6、JSP動作元素
- 7.3.6.1、jsp:include動作
- 7.3.6.2、jsp:useBean動作
- 7.3.6.3、jsp:setProperty動作
- 7.3.6.4、jsp:getProperty動作
- 7.3.6.5、jsp:forward動作
- 7.3.7、JSP隱含物件
- 7.3.8、JSP常見控制
- 7.4、EL運算式
- 7.4.1、EL概述
- 7.4.2、EL語法
- 7.4.3、EL隱含物件
- 7.4.4、EL案例演示
- 7.5、JSTL運算式
- 7.5.1、JSTL概述
- 7.5.2、JSTL依賴
- 7.5.3、JSTL常用標簽
- 第八章 Servlet3.0
- 8.1、注解開發
- 8.1.1、servlet注解
- 8.1.2、filter注解
- 8.1.3、listener注解
- 8.1.4、兩種配置同時存在
- 8.1.5、如何禁用注解組件
- 8.2、檔案上傳
- 8.3、異步處理
- 8.4、動態注冊
- 8.4.1、servlet動態注冊
- 8.4.2、filter動態注冊
- 8.4.3、listener動態注冊
- 8.5、可插性支持
配套資料,免費下載
鏈接:https://pan.baidu.com/s/1DNouClLLp4OB8mniUJGmBg
提取碼:dq2w
復制這段內容后打開百度網盤手機App,操作更方便哦
第一章 開發工具
1.1、JDK安裝
第一步:打開官網進行下載(https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html)

第二步:運行程式進行安裝
全部默認下一步即可,不用管路徑是不是存在中文和空格,無礙!
注意:如果自己會配置JDK,可以更改它的安裝路徑,那相應的環境變數也需要修改!
第三步:系統環境變數配置
此電腦 》 右鍵 》 屬性 》 高級系統設定 》 環境變數 》 系統變數 》 新建(需要新建兩個,然后修改一個)
新建兩個:JAVA_HOME代表Java的安裝目錄、CLASSPATH代表程式運行的時候,優先加載當前目錄中的類,然后再加載指定的類別庫
| 變數名 | 變數值 |
|---|---|
| JAVA_HOME | C:\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

第四步:測驗JDK是否安裝成功
打開一個cmd命令列視窗,輸入以下兩個命令查看,如果有內容輸出則證明已經配置成功!
- java -version
- javac -version

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


第二步:系統環境變數配置
此電腦 》 右鍵 》 屬性 》 高級系統設定 》 環境變數 》 系統變數 》 新建(需要新建兩個,然后修改三個)
新建兩個:CATALINA_BASE和CATALINA_HOME均代表Tomcat的安裝目錄
| 變數名 | 變數值 |
|---|---|
| CATALINA_BASE | C:\DevTools\apache-tomcat-8.5.57 (輸入自己解壓的Tomcat目錄,不要照抄我的) |
| CATALINA_HOME | C:\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

第三步:測驗Tomcat是否安裝成功
打開一個cmd命令列視窗,輸入以下一個命令查看,如果有內容輸出則證明已經配置成功!
- catalina run

- 打開瀏覽器,輸入:http://localhost:8080/

如果看到上邊這個界面了,就把剛才打開的cmd視窗關掉,否則可能會影響后邊IDEA集成Tomcat,
1.3、IDEA安裝
注意:官網的版本會隔一段時間更新一次,我這里只是告訴大家怎么下載,不一定要用最新的,我這套課程使用的是IdeaIU-2020.1.2,如果你不是這個版本,那么我建議你改為這個版本,否則后邊可能會出現一些列問題,
第一步:打開官網進行下載和安裝(https://www.jetbrains.com/idea/)


第二步:運行程式進行安裝
全部默認下一步即可,如果遇到需要一頁打勾的很多,就把有 64 的那個勾上,它代表在桌面創建一個64位的快捷方式,沒勾選也沒事,可以在開始選單打開IDEA!
第三步:關于激活的問題
由于IDEA是收費軟體,請大家自行購買激活碼,然后激活,不激活也可以試用30天!
第四步:常見的設定頁面



第五步:最終的效果圖

1.4、IDEA集成Tomcat


1.5、IDEA運行JavaWeb



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

注意:如果你有依賴的JAR包,就放到lib檔案夾中,然后添加到工程中,方便專案移動的時候,不會丟失JAR包,
至于classes是否需要創建,在這里我個人認為是不需要創建的,因為編譯器會自動創建,如果你創建了,還必須要修改配置資訊,很麻煩,所以我建議就不要創建了,一般我們也不會創建,


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

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









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


第二章 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>
樹狀結構:

支持語法:
- 嵌套標簽
- 單級標簽
- 內含屬性
注意:根節點只能有一個
注釋語法:
<!-- 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的基本資訊
- servlet-class:代表當前Servlet的具體類路徑,注意最后不包含.java
- servlet-name:代表當前Servlet的別名,可以和原Servlet名稱一樣,也可以不一樣,一般我們就一樣就行了
servlet-mapping標簽用于配置請求路徑與具體處理Servlet的對應關系
- url-pattern:這里寫你要匹配的地址路徑
- servlet-name:如果匹配到請求,該交給哪一個Servlet處理,這里的servlet-name其實就是一個Servlet的別名
第四步:啟動應用,然后瀏覽器輸入地址訪問(http://localhost:8080/myJavaWebDemo_war_exploded/MyServlet)

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

4.3、Servlet執行程序

4.4、Servlet生命周期
Servlet運行在Servlet容器中,其生命周期由容器來管理,
Servlet的生命周期通過javax.servlet.Servlet介面中的init()、service()和destroy()方法來表示,
Servlet的生命周期包含了下面4個階段:
- 類加載和實體化
- 初始化:init()
- 請求處理:service()
- 銷毀:destroy()
4.5、Servlet繼承體系
其實我們不難發現,現有的Servlet它的方法比較多,而且大多需要我們自己來實作,那有沒有一種它的實作子類,把大部分方法都是實作了,而我們只要關注請求處理就行了,那答案肯定是有的,這個類就是HttpServlet,我們只要繼承這個類重寫GET、POST方法就能實作一個簡單的Servlet請求處理,Servlet的繼承體系如下圖:

既然我們知道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需要提交表單模擬,這里就不演示了,效果都一樣!

我們注意url-pattern它可以有多種攔截形式:
- 全路徑匹配:
/a - 前半段匹配:
/a/b/c/* - 擴展名匹配:
*.action
4.6、ServletContext
每個web工程都只有一個ServletContext物件,也就是不管在哪個Servlet里面,獲取到的這個類的物件都是同一個,它用來獲取Servlet的背景關系,在服務器啟動的時候,會為托管的每一個web應用程式,創建一個ServletContext物件,當從服務器移除托管或者是關閉服務器時,ServletContext將會被銷毀,它主要有以下幾方面作用:
- 獲取全域配置引數
- 獲取web工程中的資源
- 在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)

4.6.2、獲取web工程中的資源
第一步:在 myJavaWebDemo 的 web 檔案夾中右鍵創建 a.txt 檔案,內容為 Hello,World!

第二步:在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)
注意:當你能拿到一個檔案的絕對路徑或者輸入流以后,就可以對它進行操作了!

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),查看是否登錄

在瀏覽器中訪問(http://localhost:8080/myJavaWebDemo_war_exploded/LoginServlet),模擬登錄場景

在瀏覽器中訪問(http://localhost:8080/myJavaWebDemo_war_exploded/MyHttpServlet),查看登錄次數

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)

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/)
在表單輸入資料,然后點擊提交

查看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)

如何解決回應資料中文亂碼問題?
-
以字符流輸出: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)

在瀏覽器中訪問(http://localhost:8080/myJavaWebDemo_war_exploded/DisplayServlet)

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存值狀態變更,
主要方法:

如何創建:
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存值狀態變更,
主要方法:

如何創建:
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存值狀態變更,
主要方法:

如何創建:
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執行順序
- 客戶端發出請求,先經過過濾器,如果過濾器放行,那么才能到servlet,
- 如果有多個過濾器,那么他們會按照注冊的映射順序來排隊,只要有一個過濾器不放行,那么后面排隊的過濾器以及咱們的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 | 把請求轉到一個新的頁面, |
常見屬性:
| 語法 | 描述 |
|---|---|
| id | id屬性是動作元素的唯一標識,可以在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>
屬性串列:
| 屬性 | 描述 |
|---|---|
| name | name屬性是必需的,它表示要設定屬性的是哪個Bean, |
| property | property屬性是必需的,它表示要設定哪個屬性,有一個特殊用法:如果property的值是"*",表示所有名字和Bean屬性名字匹配的請求引數都將被傳遞給相應的屬性set方法, |
| value | value 屬性是可選的,該屬性用來指定Bean屬性的值,字串資料會在目標類中通過標準的valueOf方法自動轉換成數字、boolean、Boolean、 byte、Byte、char、Character,例如,boolean和Boolean型別的屬性值(比如"true")通過 Boolean.valueOf 轉換,int和Integer型別的屬性值(比如"42")通過Integer.valueOf轉換,value和param不能同時使用,但可以使用其中任意一個, |
| param | param 是可選的,它指定用哪個請求引數作為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地址" />
屬性串列:
| 屬性 | 描述 |
|---|---|
| page | page屬性包含的是一個相對URL, page的值既可以直接給出,也可以在請求的時候動態計算,可以是一個JSP頁面或者一個 Java Servlet, |
相關示例:
<jsp:forward page="myJSP.jsp" />
7.3.7、JSP隱含物件
JSP隱式物件是JSP容器為每個頁面提供的Java物件,開發者可以直接使用它們而不用顯式宣告,JSP隱式物件也被稱為預定義變數,
JSP所支持的九大隱式物件:
| 物件 | 描述 |
|---|---|
| request | HttpServletRequest 介面的實體 |
| response | HttpServletResponse 介面的實體 |
| session | HttpSession 類的實體 |
| application | ServletContext 類的實體,與應用背景關系有關 |
| config | ServletConfig 類的實體 |
| out | JspWriter 類的實體,用于把結果輸出至網頁上 |
| pageContext | PageContext 類的實體,提供對JSP頁面所有物件以及命名空間的訪問 |
| page | 類似于Java類中的 this 關鍵字 |
| Exception | Exception 類的物件,代表發生錯誤的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個內置物件:
| 隱含物件 | 描述 |
|---|---|
| pageScope | page 作用域 |
| requestScope | request 作用域 |
| sessionScope | session 作用域 |
| applicationScope | application 作用域 |
| param | Request 物件的引數,字串 |
| paramValues | Request 物件的引數,字串集合 |
| header | HTTP 資訊頭,字串 |
| headerValues | HTTP 資訊頭,字串集合 |
| initParam | 背景關系初始化引數 |
| cookie | Cookie值 |
| 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依賴
- 匯入依賴庫:jstl.jar、standard.jar
- 引入標簽庫:
<%@ 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 社區的一片贊譽之聲:
- 新增的注解支持:該版本新增了若干注解,用于簡化 Servlet、過濾器(Filter)和監聽器(Listener)的宣告,這使得 web.xml 部署描述檔案從該版本開始不再是必選的了,
- 檔案上傳API簡化:從該版本開始,極大地簡化了檔案上傳的操作,
- 異步處理支持:有了該特性,Servlet 執行緒不再需要一直阻塞,直到業務處理完畢才能再輸出回應,最后才結束該 Servlet 執行緒,在接收到請求之后,Servlet 執行緒可以將耗時的操作委派給另一個執行緒來完成,自己在不生成回應的情況下回傳至容器,針對業務處理較耗時的情況,這將大大減少服務器資源的占用,并且提高并發處理速度,
- 動態注冊組件:在初始化ServletContext容器的時候,可以支持動態注冊三大組件,
- 可插性支持:如果說 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 ...");
}
}
屬性串列:
| 屬性名 | 型別 | 描述 |
|---|---|---|
| name | String | 指定 Servlet 的 name 屬性,如果沒有顯式指定,則該 Servlet 的取值即為類的全限定名 |
| value | String[] | 該屬性等價于 urlPatterns 屬性,兩個屬性不能同時使用 |
| urlPatterns | String[] | 指定一組 Servlet 的 URL 匹配模式 |
| loadOnStartup | int | 指定 Servlet 的加載順序 |
| initParams | WebInitParam[] | 指定一組 Servlet 初始化引數 |
| asyncSupported | boolean | 宣告 Servlet 是否支持異步操作模式 |
| description | String | 該 Servlet 的描述資訊 |
| displayName | String | 該 Servlet 的顯示名,通常配合工具使用 |
如何測驗:
打開瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/AnnotationServlet
檢測控制臺輸出:

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() {
}
}
屬性串列:
| 屬性名 | 型別 | 描述 |
|---|---|---|
| filterName | String | 指定過濾器的 name 屬性 |
| value | String[] | 該屬性等價于 urlPatterns 屬性,但是兩者不應該同時使用 |
| urlPatterns | String[] | 指定一組過濾器的 URL 匹配模式 |
| servletNames | String[] | 指定過濾器將應用于哪些 Servlet |
| dispatcherTypes | DispatcherType | 指定過濾器的轉發模式 具體取值包括: ASYNC、ERROR、FORWARD、INCLUDE、REQUEST |
| initParams | WebInitParam[] | 指定一組過濾器初始化引數 |
| asyncSupported | boolean | 宣告過濾器是否支持異步操作模式 |
| description | String | 該過濾器的描述資訊 |
| displayName | String | 該過濾器的顯示名,通常配合工具使用 |
如何測驗:
打開瀏覽器輸入:http://localhost:8080/servlet3_0_war_exploded/AnnotationServlet
檢測控制臺輸出:

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 ...");
}
}
如何測驗:
重啟服務器,觀察控制臺:


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注解:
| 屬性名 | 型別 | 是否可選 | 描述 |
|---|---|---|---|
| fileSizeThreshold | int | 是 | 當資料量大于該值時,內容將被寫入檔案 |
| location | String | 是 | 存放生成的檔案地址 |
| maxFileSize | long | 是 | 允許上傳的檔案最大值,默認值為-1,表示沒有限制 |
| maxRequestSize | long | 是 | 針對該multipart/form-data請求的最大數量,默認值為-1,表示沒有限制 |
如何測驗:



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
檢測控制臺輸出:
三秒后頁面輸出:

十秒后控制臺輸出:

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
檢測控制臺輸出:

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
檢測控制臺輸出:

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
檢測控制臺輸出:

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>
接下來我們使用一個完整示例來演示:
我們先看下我們目前已經做了哪些工程:

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









把以下代碼拷貝到組態檔中:
<?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>

創建一個需要測驗的Servlet,這里我們直接采用注解開發



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






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


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








把剛才復制到桌子上的fragment.jar復制到lib中

啟動服務器,然后輸入FragmentServlet的映射網址,看看控制臺會不會輸出

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/183434.html
標籤:其他
