JavaWeb
- Java Web基礎知識
- 資源分類
- 靜態資源:所有用戶訪問之後,得到的結果都是一樣的,比如HTML,CSS,JavaScript。 瀏覽器請求靜態資源,客戶端可以直接把靜態資源返回給瀏覽器,瀏覽器内置了靜態資源解析器可以直接解析
- 動態資源:每個用戶訪問相同資源之後,得到的結果可能不一致,比如Servlet,JSP。瀏覽器請求動態資源,服務器要把動態資源轉換成靜態資源,然後把靜態資源返回給瀏覽器
- 網路通信三要素:IP地址,端口,傳輸協議
- 常見的Java web服務器軟件
- webLogic:Oracle,大型JavaEE服務器,支持所有JavaEE規範,收費產品
- WebSphere: IBM,大型JavaEE服務器,支持所有JavaEE規範,收費產品
- JBoss:JBoss, 大型的JavaEE服務器,支持所有JavaEE規範,收費產品
- Tomcat: Apache, 中小型JavaEE服務器,支持少量的JavaEE規範,比如servlet,jsp,免費的
- Web項目結構
src
目錄放java代碼web
目錄放各種資源WEB-INF
目錄放web.xml
,claasses
目錄(設置爲output),lib
目錄(設置為jar directory,注意爲了讓Idea識別Servlet接口,要把Tomcat的lib目錄下的servlet-api.jar
放到lib目錄中)。WEB-INF
目錄下的資源不能被瀏覽器訪問到
- 資源分類
- Tomcat
- 啓動:windows上面用
startup.bat
, linux上面用startup.sh
。 如果沒有配置JAVA_HOME
, 那麽startup.bat
不能用 - 關閉:直接關閉
startup.bat
的窗口,應該用shutdown.bat
或者在startup.bat
中用Ctrl+C
關閉 - 部署項目的方式
- 方法1:把項目放在Tomcat安裝目錄的
webapps
目錄下面。 或者把項目打包成.war
文件,放在Tomcat安裝目錄的webapps
目錄下面,Tomcat會把.war
文件自動解壓縮。 這種方法虛擬目錄名字就是項目名字 - 方法2:修改Tomcat的
conf
目錄下的server.xml
文件,在<Host>
内部添加一個<Context docBase=項目實際存放路徑 path=項目訪問路徑(虛擬路徑)/>
標簽。 這種方法虛擬目錄名字就是path
設置的虛擬目錄名字 - 方法3:在
Tomcat根目錄/conf/Catalina/localhost
目錄下面新建一個xml文件,在上面添加一個<Context docBase=項目實際存放路徑/>
標簽。這個方法虛擬目錄名字就是xml文件名字。 這個是熱部署方式,推薦使用
- 方法1:把項目放在Tomcat安裝目錄的
- 訪問項目方式:
localhost/虛擬目錄/項目資源
- IDEA新建JavaWeb項目并且集成Tomcat
- 新建Java項目,項目右鍵選擇
Add Framework Support
, 選擇Web Application
項目,勾選create web.xml
Run
->Edit Configurations
-> 左邊加號選擇Tomcat Server Local
-> 右邊Server
下面選擇Tomcat服務器版本, 在On Update action
和On frame deactivation
選擇Update classes and resources
;在Deployment
下面添加當前項目,根據需求修改Application context
(虛擬目錄名字)
- 新建Java項目,項目右鍵選擇
- 啓動:windows上面用
- Servlet
- Servlet是server applet的縮寫,意思是運行在服務器的小程序
- 瀏覽器訪問一個動態資源的時候,需要在服務器上有一個類來處理請求,這個類就是Servlet的實現類。Servlet是一個接口,定義了Java類被瀏覽器訪問到(被Tomcat識別到)的規則,我們需要實現Servlet接口,重寫對應的方法
- XML配置Servlet:
爲了讓資源和Servlet類對應起來,需要在
web.xml
中配置servlet和servlet-mapping1
2
3
4
5
6
7
8
9
10
11
12
13
14<!-- 配置Servlet類 -->
<servlet>
<!-- name是Servlet的識別名稱 -->
<servlet-name>intro</servlet-name>
<!-- class是Servlet類的全限定類名 -->
<servlet-class>com.yanxuanshaozhu.web.servlet.ServletDemo</servlet-class>
</servlet>
<!-- 配置Servlet類和資源路徑的對應 -->
<servlet-mapping>
<!-- Servlet類的識別名稱 -->
<servlet-name>intro</servlet-name>
<!-- 資源路徑的URL,以/開頭 -->
<url-pattern>/intro</url-pattern>
</servlet-mapping> - Servlet3.0及以上版本支持用注解配置Servlet,不需要
web.xml
:在類上面使用@WebServlet(urlPatterns = "/資源路徑")
,簡寫為@WebServlet("/資源路徑")
。一個注解可以定義多個訪問路徑:@WebServlet("/資源路徑1", "/資源路徑2, ..., "/資源路徑n")
, 資源路徑也可使使用通配符,例如@WebServlet("/*")
- 訪問資源和Sevlet對應:服務器收到請求後解析訪問的URL,遍歷
web.xml
搜索對應的url-pattern
標簽,然找到servlet-class
對應的類名,把字節碼加載到服務器内存中,然後通過反射創建實例,調用類中的方法 - Servlet的生命周期
- 創建:
- 默認第一次資源被訪問的時候,Servlet被創建,然後調用
init
方法,init
方法只執行一次。 - 可以通過配置把Servlet改成在啓動服務器后創建,創建後調用
init
方法,需要在web.xml
進行如下配置:1
2
3
4<servlet>
<!-- 啓動時創建,n>= 0;第一次訪問創建,n < 0。默認值n = -1,默認不用配置這個-->
<load-on-startup>n</load-on-startup>
</servlet> - Servlet只會創建一次,是單例對象,多綫程創建可能會有安全問題。所以不要在Servlet類中定義成員變量,防止多綫程共享數據不一致問題
- 默認第一次資源被訪問的時候,Servlet被創建,然後調用
- 提供服務:
每次資源被訪問的時候,
service
方法被調用,這個方法可以被多次調用 - 銷毀:
- 在服務器正常關閉的時候,Servlet被銷毀,然後調用
destroy
方法,destroy
方法只執行一次 - 如果服務器不是正常關閉,
destroy
方法也不會被調用
- 在服務器正常關閉的時候,Servlet被銷毀,然後調用
getServletConfig()
:獲取Servlet配置,getServletInfo
:獲取Servlet信息
- 創建:
- Servlet體系結構
Servlet
是接口GenericServlet
是實現Servlet
接口的抽象類,它對Servlet接口方法做了默認空實現,所以繼承GenericServlet
只需要重寫service
方法HttpServlet
是繼承GenericServlet
抽象類,他是對HTTP協議的封裝,繼承HttpServlet
一般只需要重寫doGet
、doPost
方法1
2
3
4
5
6
7
8
9
10
11
12
13
public class HttpWebServlet extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGot!");
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPosted!");
}
}
- HTTP概述
- HTTP是基於TCP/IP的高級協議,默認端口號是80,基於請求響應模型(請求和相應一一對應), 無狀態(每次請求是獨立的,不能交互數據)
- 請求消息格式
- 請求行:格式是 "請求方式 URL 請求協議/版本",
- 請求頭:格式是 "key:value",
User-Agent
表示瀏覽器版本信息,服務器可以用這個來解決兼容性問題,Referer
:告訴服務器當前請求從哪裏來(防止盜鏈,便於統計) - 請求空行: 空行,用來分割請求頭和請求體
- 請求體: 格式是"key=value",GET方式請求參數在URL後面,POST方式請求參數在請求體中
- Request對象
ServletRequest
對象和ServletResponse
對象:收到請求後,服務器創建req和res對象,把請求消息封裝到req中,把req和res傳給service方法,在service方法中用req獲取請求消息,對請求進行處理,把響應消息封裝到res中,最後服務器從res中拿出響應消息返還給瀏覽器ServletRequest
和HttpServletRequest
都是接口,RequestFacade
實現了HttpServletRequest
接口,這個類就是Tomcat用的實現類- Request對象功能
- 獲取請求數據
- 獲取請求行:
req.getMethod()
獲取請求方式,req.getContextPath()
獲取虛擬目錄,req.getServletPath()
獲取Servlet路徑,req.getQueryString()
獲取請求參數,req.getRequestURL()
獲取請求URL,req.getProtocol()
獲取請求協議和版本,req.getRemoteAddr()
獲取客戶端ip地址 - 獲取請求頭:
req.getHeader(key)
通過請求頭名稱獲取請求頭值,req.getHeaderNames()
獲取所有請求頭名稱1
2
3
4
5
6Enumeration<String> names = req.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = req.getHeader(name);
System.out.println(name + " => " + value);
} - 獲取請求體:請求體數據是流,需要先獲取流,再獲取具體數據。
req.getReader()
獲取字符流數據,只適用於字符數據,req.getInputStream()
獲取字節流流數據,適用於所有數據類型 - 獲取請求參數的通用方法:
req.geParameter(key)
:獲取value,req.getParameterNames()
:獲取所有key,req.getParameterValues(key)
:獲取所有value,適用於複選框,req.getParameterMap()
:獲取參數的k-v組合
- 獲取請求行:
- 請求轉發
- 請求轉發是在服務器内部的一種資源跳轉方式,Servlet需要各自功能邏輯相對獨立,因此一個請求可能需要多個Servlet進行協作,因此就需要請求轉發
- 流程:
- 通過request對象獲取請求轉發器對象:
req.getRequestDispatcher(redirectTargetPath)
- 通過請求轉發器進行轉發:
dispatcher.forward(req, res)
- 通過request對象獲取請求轉發器對象:
- 轉發的特點
- 轉發之後瀏覽器地址欄路徑沒有變化(轉發是服務器内部的,客戶端不知道發生了轉發)
- 轉發只能轉發到服務器内部
- 轉發的時候客戶端只對服務器發送了一次請求
- 數據共享
- 域對象:一個有作用範圍的對象,範圍内部可以共享數據
- request域:一次請求的範圍,因爲轉發只有一次請求,因此多個Servlet可以共享數據
- request實現共享數據:
req.setAttribute(key, value)
,一個Servlet設置了req屬性之後,請求轉發的另一個servlet可以通過req.getAttribute(key)
獲取
- 獲取
ServletContext
對象: 通過req.getServletContext()
獲取
- 獲取請求數據
- 一個登錄案例
- 用戶登錄:成功顯示用戶名成功,失敗顯示失敗
- 邏輯:用戶html頁面提交post請求到loginServlet,在裏面用把請求參數封裝成User對象,通過UserDao和數據庫交互,拿到返回的User對象,如果不是null,把返回的User對象封裝到request中,并且通過請求轉發器轉發到successServlet,輸出成功結果,如果是null,通過請求轉發器轉發到failServlet,輸出失敗結果
- 頁面顯示中文:
resp.setContentType("text/html;charset=utf-8");
可以,但是resp.setCharacterEncoding("utf-8");
不行 - UserDao放在dao目錄下面,使用JdbcTemplate和數據庫進行交互
- User放在domain目錄下面
- JDBCUtils放在util目錄下面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public class JDBConnector {
private static ComboPooledDataSource ds = new ComboPooledDataSource();
// 初始化連接池
static {
Properties prop = new Properties();
try {
BufferedReader buffer = new BufferedReader(new FileReader("D:\\MyWork\\Java\\JavaWeb\\LoginDemo\\src\\db.properties"));
prop.load(buffer);
ds.setDriverClass(prop.getProperty("driver"));
ds.setJdbcUrl(prop.getProperty("url"));
ds.setUser(prop.getProperty("username"));
ds.setPassword(prop.getProperty("password"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連接池對象
*/
public static DataSource getDataSource() {
return ds;
}
/**
* 獲取鏈接對香港
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
- 響應消息格式
- 響應行
- 響應行格式: "協議/版本 響應狀態碼 響應狀態碼描述"
- 響應狀態碼:
- 1xx: 服務器接受數據沒接收完成,等待后發送1xx給客戶端
- 2xx: 成功
- 3xx: 302重定向,304訪問緩存
- 4xx: 客戶端錯誤,404請求路徑沒有資源,405請求方式在服務器沒有對應了方法
- 5xx: 服務器錯誤,500服務器内部異常
- 響應頭:格式是 "key:value",
Content-Type
服務器告訴客戶端響應躰數據格式和編碼格式,Content-disposition
服務器告訴客戶端以什麽格式打開響應躰數據,比如in-line
或者attachment
- 響應空行: 空行,用來分割響應頭和響應躰
- 響應體: 服務器返回給客戶端的響應數據
- 響應行
- Response對象
ServletResponse
和HttpServletResponse
是接口- Response對象功能
- 設置響應消息
- 設置響應狀態碼:
res.setStatus(int sc)
- 設置響應頭:
res.setHeader(String name, String value)
- 設置響應躰
- 獲取輸出流
- 字符輸出流:
res.getWriter()
- 字節輸出流:
res.getOutputStream()
- 字符輸出流:
- 使用輸出流,將數據輸出到客戶端
- 獲取輸出流
- 設置響應狀態碼:
- 重定向
- 定義:瀏覽器請求服務器,服務器告訴瀏覽器重定向到另一個地址,就是重定向
- 重定向步驟
- 瀏覽器設置響應狀態碼302:
res.setStatus(302)
- 瀏覽器在響應頭中設置key是location,
value是重定向目標的新資源地址:
res.setHeader("location", "/虛擬目錄/新資源地址")
- 另一種方法:因爲重定向和狀態碼和響應頭key是固定的,只需要傳入重定向地址,因此可以直接使用
req.sendRedirect(String location)
即可完成重定向
- 瀏覽器設置響應狀態碼302:
- 重定向特點
- 重定向瀏覽器地址欄發生變化
- 重定向可以訪問外部資源
- 重定向至少兩次請求,不能使用request域對象來共享數據
- 路徑的寫法
- 相對路徑
- 絕對路徑:如果請求是從客戶端訪問的,絕對路徑是
/虛擬目錄/資源路徑
,虛擬目錄使用req.getContextPath()獲取
,比如重定向中的路徑;如果請求是從服務器訪問的,絕對路徑是/資源路徑
,比如轉發中的路徑
- 設置驗證碼
- 驗證碼是一張圖片,目的是防止惡意表達注冊
- 驗證碼可以從圖庫中挑選,也可以用程序隨機生成: 基礎的可以把程序生成的驗證碼圖片通過字節流輸出到瀏覽器中
- 生成驗證碼的Java代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 創建圖片
// 單位是pixel
int width = 100;
int height = 50;
// 在内存中創建驗證碼圖片
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 2. 美化圖片
// 獲取畫筆
Graphics g = img.getGraphics();
// 設置畫筆顔色
g.setColor(Color.pink);
// 畫筆畫一個舉行填充背景顏色
g.fillRect(0, 0, width, height);
g.setColor(Color.blue);
g.drawRect(0, 0, width - 1, height - 1);
// 設置驗證碼文字内容
g.setColor(Color.black);
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxya0123456789";
Random rand = new Random();
for (int i = 1; i <= 4; i++) {
int index = rand.nextInt(base.length());
String sub = base.charAt(index) + "";
g.drawString(sub, width / 5 * i, height / 2);
}
// 加上干擾綫
g.setColor(Color.green);
for (int i = 0; i < 10; i++) {
int x1 = rand.nextInt(width);
int x2 = rand.nextInt(width);
int y1 = rand.nextInt(height);
int y2 = rand.nextInt(height);
g.drawLine(x1, x2, y1, y2);
}
// 3. 將圖片輸出到瀏覽器頁面上
ImageIO.write(img, "jpg", resp.getOutputStream());
} - 在瀏覽器中點擊更換驗證碼圖片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
window.onload = () => {
let img = document.getElementById("verificationcode");
let div = document.getElementById("change");
// 因爲瀏覽器對同一個地址會緩存,所以要對地址加上一段無重複數據保證加載的是新生成的驗證碼
div.onclick = () => {
let time = new Date().getTime();
img.src = "/verificationCode?" + time;
}
}
</script>
</head>
<body>
<img src="/verificationCode" id="verificationcode">
<br>
<div id="change">點擊切換驗證碼</div>
</body>
</html>
- 設置響應消息
ServletContext
對象- 概念:代表整個web應用,可以和程序的容器(服務器)進行通信
- 獲取
ServletContext
對象- 通過了request對象獲取:
req.getServletContext()
- 通過HttpServlet對象獲取:
this.getServletContext()
- 通過了request對象獲取:
- 功能
- 獲取MIME類型
- MIME類型是在互聯網通信過程中定義的一種文本數據類信得過
- MIME類型格式:
大類型/小類型
, 如:text/html
,image/jpeg
- 獲取MIME類型的方法:
context.getMimeType(String fileName)
,實際上通過擴展名來獲取的
- 共享數據
ServletContext
對象是一個域對象,可以共享數據context.getAttribute(String name)
,context.setAttribute(String name, Object value)
,context.removeAttribute(String name)
- 域範圍:可以共享服務器上所有的數據, 比如一個Servlet可以訪問另一個Servlet的數據
- 獲取文件的真實(服務器)路徑
- 意義:先獲取到配置文件在服務器上的真實路徑,然後再讀取配置文件
- 獲取方法:
context.getRealPath(String path)
- 如果文件在
web
目錄下面,那麽path
就是/文件名
- 如果文件在
web/WEB-INFO
目錄下面,那麽path
就是/WEB-INFO/文件名
- 如果文件在
src
目錄下面,那麽path
就是/WEB-INFO/classes/文件名
,因爲src
所有的文件會被服務器放到web/classes
目錄下面
- 如果文件在
- 在非Servlet類中讀取文件路徑
1
2
3
4
5Properties prop = new Properties();
// 直接寫db.properties,db.properties文件放在src下面即可
String path = TestDemo.class.getClassLoader().getResource("db.properties").getPath();
FileInputStream fis = new FileInputStream(path);
prop.load(fis);
- 獲取MIME類型
- 會話
- 基本概念
- 定義:瀏覽器第一次給服務器資源發送請求,會話建立,知道有一方斷開爲止
- 一次會話中包含多次請求和響應
- 會話技術的實現
- 客戶端會話技術: Cookie
- 服務器端會話技術: Session
- Cookie
- 定義:將數據保存在客戶端的會話技術
- Cookie作用
- 一般用於存儲少量不敏感的數據
- 在不登陸的情況下,完成服務器對客戶端的身份識別
- 流程:
- 服務器創建Cookie,在response中返回給瀏覽器:
new Cookie(String name, String value)
,resp.addCookie(Cookie cookie)
- 瀏覽器收到Cookie中進行存儲,在下一次發送請求的時候會自動把Cookie發送給服務器
- 服務器收到請求,拿到Cookie中的數據:
req.getCookies()
拿到Cookie數組,cookie.getName()
,cookie.getValue()
拿到Cookie的key和value
- 服務器創建Cookie,在response中返回給瀏覽器:
- Cookie實現原理
- 瀏覽器收到相應,響應頭:
set-cookie:key=value
,瀏覽器會把這個數據保存到本地Cookie中 - 瀏覽器第二次發送請求,請求頭:
cookie:key=value
- 瀏覽器收到相應,響應頭:
- Cookie的相關細節
- 服務器可以創建多個Cookie,多次調用
resp.addCookie(Cookie cookie)
把所有Cookie放到響應中 - 默認Cookie只存在瀏覽器内存中,瀏覽器關閉之後Cookie就消失了;持久化Cookie需要使用
cookie.setMaxAge(int secondes)
設置Cookie的存活時間,設置為0代表刪除cookie,設置正數代表存活時間,設置負數代表會話cookie,瀏覽器關閉cookie就消失了 - Tomcat的cookie不支持特殊字符,需要手動把格式轉換成沒有特殊字符的格式
- 服務器可以創建多個Cookie,多次調用
- Cookie共享
- 一個Tomcat服務器上的多個項目之間默認是不可以共享Cookie的
cookie.setPath(String path)
可以設置Cookie的路徑,默認這個路徑是當前項目的虛擬目錄,可以設置成服務器根路徑/
,這樣服務器上所有項目可以共享這個Cookie
- Cookie的特點
- 數據存在瀏覽器,容易丟失,安全性也比服務器差
- Cookie對單個Cookie的大小有限制,對同一個域名下的Cookie的總數量也有限制
- Session
- 定義:將數據保存在服務器的會話技術
- Session作用:在一次會話的多次請求之間共享數據
- 獲取
HttpSession
對象:req.getSession()
HttpSession
對象相關方法:getAttribute(String name)
,setAttribute(String name, Object value)
,removeAttribute(String name)
- Session原理
- 服務器需要確保一次會話的多次請求之間的Session對象是同一個,這樣才能獲得存在Session中的數據
- Session是通過Cookie實現的:第一次請求的響應頭的set-cookie中有Session的唯一id,之後的請求的請求頭的cookie中有帶著這個session的id,服務器會在自己内存中查找對應的session對象
- Session的相關細節
- 客戶端關閉了,服務器沒關閉,獲取的Session是不是同一個
- 默認不是同一個,因爲客戶端關閉,會話已經關閉了
- 如果要訪問同一個Session,需要創建Cookie,
new Cookie("JSESSIONID", session.getId())
并且設置Cookie的有效期
- 服務器關閉了,客戶端沒關閉,獲取的Session是不是同一個
- 默認不是同一個,因爲服務器内存中的Session對象被銷毀了
- 服務器關閉,Session被銷毀,但是Session中的數據不應該丟失,需要Session的鈍化和活化技術
- Session鈍化:在服務器關閉之前,把服務器内存的Session對象序列化到硬盤之上
- Session活化:在服務器啓動之後,把硬盤上的Session對象反序列化到服務器内存中
- IDEA配置的Tomcat服務器不能完成Session鈍化和活化,但是原生的Tomcat會自動完成Session的鈍化和火花
- 銷毀Session
- 服務器被關閉Session被銷毀
session.invalidate()
方法可以銷毀自己- Session對象的默認失效時間是30分鐘
- 修改所有項目的Session失效時間:
/Tomcat根目錄/conf/web.xml
中修改<session-timeout>失效時間的分鐘數</session-out>
- 修改本項目的Session失效時間:
/WEB-INF/web.xml
中修改<session-timeout>失效時間的分鐘數</session-out>
- 修改所有項目的Session失效時間:
- Session的特點
- 用於存儲一次會話的多次請求之間的數據,存在服務器的内存中
- Session可以存儲任意類型,任意大小的數據
- 客戶端關閉了,服務器沒關閉,獲取的Session是不是同一個
- 基本概念
- JSP
- JSP(Java server pages):java服務器頁面,既可以定義html標簽,也可以直接寫java代碼
- JSP的原理
- 瀏覽器請求jsp文件資源,服務器把jsp變成對應的java文件,然後編譯成class文件
- 瀏覽器實際訪問的是class字節碼文件
- JSP實際上是一個Servlet
- JSP脚本:需要注意的是,脚本是可以被截斷,從而配合HTML標簽一起使用的
<% 代碼%>
: 定義的Java代碼在Servlet的service方法内部<%! 代碼%>
:定義的Java代碼是Servlet的成員變量或者成員方法<%= 代碼%>
: 定義的Java代碼會輸出到頁面上
- JSP内置對象
- JSP有9個内置對象:
- 域對象:
pageContext
(當前頁面共享數據,也可以用來獲取其他8個内置對象),request
(一次請求訪問多個資源共享數據,轉發會用到),session
(一次會話的多個請求之間共享數據,重定向會用到),application
(所有用戶共享數據,服務器上的單例對象) - 其他對象:
response
,out
(輸出内容到頁面上),page
(相當於java的this,指當前Servlet - ),
configure
(Servlet的配置對象),exception
- 域對象:
out.write()
寫在頁面什麽位置就在什麽位置輸出,resp.getWriter().write()
會在頁面最前面輸出,因爲tomcat會先訪問response對象緩衝區,再訪問out對象緩衝區。所以一般不要在jsp中使用resp.getWriter().write()
,而是使用out.write()
- JSP有9個内置對象:
- JSP指令
- 作用: 用來配置JSP頁面,導入資源文件
- 格式:
<%@ 指令名稱 屬性名1=屬性值1 屬性名2=屬性值2 %>
- 指令分類
<%@ page %>
: 配置JSP頁面的屬性- 頁面總體設置
<%@ page contentType"text/html;charset=utf-8" pageEncoding="utf-8" language="java" buffer="16kb" %>
- 導入需要的Java包,每一個Java包單獨寫一個標簽:
<%@ page import="java包名" %>
- 設置錯誤頁面:
<%@ page errorPage="錯誤頁面路徑" isErrorPage="true/false" %>
, 默認isErrorPage
設置的是false,如果設置成爲true,那麽就可以在頁面使用exception
内置對象
- 頁面總體設置
<%@ taglib %>
: 配置JSP頁面包含的内容- 格式:
<%@ taglib prefix="前綴" uri="URI" %>
- 導入JSTL標簽庫
- 把
jakarta.servlet.jsp.jstl-2.0.0.jar
和jakarta.servlet.jsp.jstl-api-2.0.0.jar
放到WEB-INF/lib
中 - 加上
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
標簽
- 把
- 一般用來導入標簽庫
- 格式:
<%@ include %>
: 導入資源文件- 格式:
<%@ include file="文件路徑名" %>
- 這個可以用來設置頁面格式模板
- 格式:
- JSP注釋
- HTML注釋:只能注釋HTML代碼片段,寫法是
<!-- -->
- JSP注釋:可以注釋所有内容,寫法是
<%-- --%>
- HTML注釋:只能注釋HTML代碼片段,寫法是
- 一個Servlet的方法改寫成jsp的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
Cookie[] res = req.getCookies();
boolean flag = false;
if (res != null && res.length > 0) {
for (Cookie cookie : res) {
if ("lasttime".equals(cookie.getName())) {
cookie.setMaxAge(60 * 60 * 24 * 30);
String value = cookie.getValue();
resp.getWriter().write("欢迎回来,上次登陆时间 " + value);
String pattern = "MM-dd-yyyy";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
String date = formatter.format(new Date());
cookie.setValue(date);
resp.addCookie(cookie);
flag = true;
break;
}
}
}
if (flag == false || res == null || res.length == 0) {
String pattern = "MM-dd-yyyy";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
String date = formatter.format(new Date());
Cookie cookie = new Cookie("lasttime", date);
cookie.setMaxAge(60 * 60 * 24 * 30);
resp.addCookie(cookie);
resp.getWriter().write("你好,欢迎登陆!");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
Cookie[] res = request.getCookies();
boolean flag = false;
if (res != null && res.length > 0) {
for (Cookie cookie : res) {
if ("lasttime".equals(cookie.getName())) {
cookie.setMaxAge(60 * 60 * 24 * 30);
String value = cookie.getValue();
%>
<!-- 這裏截斷了java代碼,加入了HTML標簽,并且用<%=value%>輸出結果到頁面上 -->
<h1>"欢迎回来,上次登陆时间 " <%=value%></h1>
<%
String pattern = "MM-dd-yyyy";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
String date = formatter.format(new Date());
cookie.setValue(date);
response.addCookie(cookie);
flag = true;
break;
}
}
}
if (flag == false || res == null || res.length == 0) {
String pattern = "MM-dd-yyyy";
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
String date = formatter.format(new Date());
Cookie cookie = new Cookie("lasttime", date);
cookie.setMaxAge(60 * 60 * 24 * 30);
response.addCookie(cookie);
%>
<h1>"你好,欢迎登陆!"</h1>
<%
}
%>
</body>
</html> - 登錄+輸入驗證碼的例子
- 實現邏輯
- 登錄Servlet和驗證碼Servlet是不同的Servlet,要保存驗證碼内容到Session中
- 登錄成功,重定向到成功頁面,要保存成功信息到Session對象中
- 登錄失敗,轉發到登陸頁面,要保存失敗的信息數據到request的屬性中
- LoginServlet代碼實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String verificationCode = req.getParameter("verificationCode");
String codeString = (String)req.getSession().getAttribute("verificationCodeString");
// 忽略大小寫的比較
if (!verificationCode.equalsIgnoreCase(codeString)) {
// 驗證碼錯誤,轉發到登陸頁面
req.setAttribute("code_error", "驗證碼錯誤");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
} else {
User user = new User();
user.setUsername(username);
user.setPassword(password);
UserDao dao = new UserDao();
User res = dao.login(user);
if (res == null) {
// 用戶名或密碼錯誤,轉發到登陸頁面
req.setAttribute("login_error","用戶名或密碼錯誤");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
} else {
// 登陸成功,重定向到成功頁面
req.getSession().setAttribute("user", username);
resp.sendRedirect(req.getContextPath()+"/success.jsp");
}
}
}
- 實現邏輯
- MVC開發模式
- 歷史發展
- 最開始全部用response輸出標簽數據,非常麻煩
- 有了JSP之後,簡化了Servlet的開發,如果過度使用JSP,在JSP寫了大量Java代碼,難以維護,難以分工協作
- 之後JavaWeb藉鑒了MCV開發模式,使得程序設計更加合理
- MVC
- Model,模型,完成具體業務操作,實際上是JavaBean
- View,視圖,展示數據,實際上是JSP頁面
- Controller,控制器,接受輸入,調用模型,將數據交給View展示,實際上是Servlet
- 瀏覽器請求數據,首先Controller接受請求,然後調用Model進行業務操作,得到結果之後Controller把結果交給View進行展示
- 優缺點
- 優點:耦合度點,方便維護,利於分工協作;重用性高
- 缺點:使得項目結構變得複雜,便於分工協作
- 歷史發展
- EL(Expression Language)表達式
- 背景:JSP值進行數據展示,不應該有大量Java代碼,這些代碼有兩個替代選擇:EL表達式和JSTL標簽
- 目的:替代和簡化JSP頁面中Java代碼的編寫
- 語法:
${表達式}
- JSP默認支持EL表達式,有兩種方式可以忽略EL表達式
- 忽略頁面中所有EL表達式:
<%@ page isELIgnored="true" %>
- 忽略頁面中單個EL表達式:
\${表達式}
,反斜杠轉義
- 忽略頁面中所有EL表達式:
- EL表達式的功能
- 進行運算:
/
或者div
,%
或者mod
,&&
或者and,||
或者or,!
或者not
,empty
(判斷字符串,集合,數組是否是null或者是空的) - 從域對象中獲取值:
${域名稱.key名稱}
,域名稱是pageScope
,requestScope
,sessionScope
,applicationScope
; 或者${key名稱}
,從範圍小到範圍大依次尋找屬性值(page -> request -> session -> application)- 獲取對象的屬性值:
${域名稱.key名稱.value對象的屬性名稱}
,本質上會調用對應屬性的getter方法,所以對應的Java類裏面可以只有getter方法但是這個名字的屬性並不存在 - 獲取List的值:
${arr[index]}
,如果index越界,不會報錯,結果是空字符串 - 獲取Map的值:
${map.key名稱}
或者${map["key名稱"]}
,如果key不存在,不會報錯,結果是空字符串
- 進行運算:
- EL表達式隱式對象
- EL有11個隱式對象
pageContext
對象可以獲取JSP的其他8個内置對象,比如pageContext.request
拿到了request對象,pageContext.request.contextPath
在JSP頁面中動態的獲取虛擬目錄
- JSTL(Java Server Pages Library)標簽
- 目的:替換和簡化JSP頁面中Java代碼的標簽
- 使用步驟
- 導入jstl相關jar包:
把
jakarta.servlet.jsp.jstl-2.0.0.jar
和jakarta.servlet.jsp.jstl-api-2.0.0.jar
放到WEB-INF/lib
中 - 通過
<%@ taglib %>
導入標簽庫:<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 導入jstl相關jar包:
把
- 常用的JSTL標簽
if
標簽: 判斷boolean表達式,如果是true則顯示標簽内容到頁面,否則不顯示1
2
3
4
5
6
7
8
9
10<%
request.setAttribute("number", 3);
%>
<c:if test="${requestScope.number % 2 !=0}">
${requestScope.number}是奇數
</c:if>
<!-- 因爲if標簽沒有else,需要手寫if來模擬else的情況 -->
<c:if test="${requestScope.number % 2 ==0}">
${requestScope.number}是偶數
</c:if>choose
標簽: 判斷表達式的值,如果匹配某一個when
標簽,則輸出匹配的值,如果沒有匹配,輸出otherwise
標簽的值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<%
Random random = new Random();
int day = random.nextInt(1, 10);
request.setAttribute("day", day);
%>
<c:choose>
<c:when test="${requestScope.day == 1}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 2}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 3}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 4}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 5}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 6}">星期${requestScope.day}</c:when>
<c:when test="${requestScope.day == 7}">星期${requestScope.day}</c:when>
<c:otherwise>數字${requestScope.day}輸入有無</c:otherwise>
</c:choose>foreach
標簽- 完成重複操作
1
2
3for (int i = 0; i < 10; i+= 2) {
System.out.println(i);
}1
2
3<c:forEach begin="0" end="9" var="i">
${i}
</c:forEach> - 遍歷容器
1
2
3for (Integer i: list) {
System.out.println(i);
}1
2
3
4
5
6<c:forEach items="${list}" var="i" varStatus="s">
${i} <br>
${s.index}<br> <!--容器中元素的索引,從0開始 -->
${s.count}<br> <!-- 循環次數,從1開始-->
</c:forEach>
- 完成重複操作
- 三層架構
- 表示層(界面層/Web層): 用戶可以通過表示層的組件和服務器進行交互
- 業務層(Service層):處理業務邏輯,組合Dao層的簡單方法,組成業務邏輯
- 持久層(數據訪問層/Dao層): 操作數據存儲文件,和數據庫交互,實現各種CRUD方法
- Filter
- 作用: 一般用於完成一些通用的操作,例如登陸驗證,統一編碼處理,敏感字符過濾
- JavaWeb三大組件:Servlet, Filter, Listener
- 配置過濾器
- 定義一個類,實現接口Filter:
jakarta.servlet.Filter
- 重寫繼承方法:
init()
,doFilter()
,destroy()
,在doFilter()
方法内部需要調用filterChain.doFilter(servletRequest, servletResponse);
對能夠訪問的目標資源放行,否則是訪問不到目標資源的 - 配置過濾器攔截路徑:
- 使用注解配置:
@WebFilter(urlPatterns={"攔截路徑"})
- 使用
web.xml配置
: 和Servlet配置非常相似1
2
3
4
5
6
7
8<filter>
<filter-name>自定義Filter名稱</filter-name>
<filter-class>實現Filter的類的全類名</filter-class>
</filter>
<filter-mapping>
<filter-name>filter標簽中的Filter名稱</filter-name>
<url-pattern>想要Filter生效的URL攔截路徑</url-pattern>
</filter-mapping>
- 使用注解配置:
- 定義一個類,實現接口Filter:
- Filter執行流程:
- 請求被Filter攔截,一般在這裏對request進行增强
- Filter使用
filterChain.doFilter(req, resp)
會傳遞給下一個Filter,如果沒有下一個Filter就執行目標資源 - 經過
doFilter()
方法之後,resp
對象已經是有内容的了,一般在這裏對response進行增强
- Filter生命周期
- 服務器啓動之後會創建Filter對象,然後調用
init
方法,這個方法只執行一次 - 每次請求被攔截的時候,
doFilter
方法被調用,這個方法會執行多次 - 服務器關閉之後,Filter被銷毀,如果服務器是正常關閉,在Filter銷毀之前會調用
destroy
方法,這個方法只執行一次
- 服務器啓動之後會創建Filter對象,然後調用
- Filter配置攔截路徑
- 具體資源路徑:
比如
/index.jsp
,只有訪問這個具體資源的時候,Filter才會執行 - 設置攔截目錄:
比如
/user/*
,訪問攔截目錄下的所有資源,Filter都會執行 - 後綴名攔截:
比如
*.jsp
(注意這裏沒有/
),訪問所有帶這個後綴名的資源,Filter都會執行 - 攔截所有資源:
/*
,訪問所有資源的時候,Filter都會執行
- 具體資源路徑:
比如
- Filter配置攔截方式
- 使用注解配置:
在
@WebFilter
中指定dispatcherTypes
屬性,有DispatcherType.REQUEST
(默認值,攔截請求),DispatcherType.FORWARD
(攔截轉發),DispatcherType.INCLUDE
(包含訪問資源,include控制權在第一個Servlet,forward控制權在第二個Servlet),DispatcherType.ERROR
(錯誤跳轉),DispatcherType.ASYNC
(異步訪問)。因爲dispatcherTypes
屬性是一個數組,所以可以指定多個攔截方式 - 使用
web.xml
配置:1
2
3
4
5
6<filter-mapping>
<filter-name>filter標簽中的Filter名稱</filter-name>
<url-pattern>想要Filter生效的URL攔截路徑</url-pattern>
<!-- 加入下面這一行 -->
<dispatcher>攔截方式(比如REQUEST)</dispatcher>
</filter-mapping>
- 使用注解配置:
在
- FilterChain
- 執行順序
- 注解配置的Filter:
比較各個Filter的類名的字符串大小,小的先執行,比如
Filter17
比Filter6
先執行 web.xml
配置的Filter: 按照文件中<filter-mapping>
標簽的先後順序執行
- 注解配置的Filter:
比較各個Filter的類名的字符串大小,小的先執行,比如
- 執行順序
- 案例:登陸驗證
- 登陸驗證是一種權限控制
- 需求:登陸了就訪問,沒登陸跳轉登錄頁面
- 邏輯:
- 登錄相關的資源直接放行
- 其他資源判斷是否登錄決定是否放行: 檢查Session中的登陸狀態即可
- Listener
- JavaWeb三大組件之一
- 事件監聽機制
- 需要有事件,事件源,監聽器,注冊監聽(把事件,事件源和監聽器綁定在一起,黨事件源發生事件之後,執行監聽器代碼)
jakarta.servlet.ServletContextListener
- 這個是一個接口,需要手動實現相關的類
- 監聽ServletContext對象的創建和銷毀,服務器啓動,ServletContext自動創建,服務器關閉,ServletContext自動銷毀
default void contextInitialized(ServletContextEvent sce)
: 在ServletContext對象創建之後調用default void contextDestroyed(ServletContextEvent sce)
: 在ServletContext對象被銷毀之後調用
- 實現監聽器
- 寫一個類實現
ServletContextListener
接口 - 重寫
contextInitialized(ServletContextEvent sce)
方法和contextDestroyed(ServletContextEvent sce)
方法 - 配置監聽器
- 使用
web.xml
配置1
2
3<listener>
<listener-class>監聽器實現類的全類名</listener-class>
</listener> - 使用注解配置: 加入
@WebListener
注解即可
- 使用
- 寫一個類實現
- 使用監聽器加載資源文件
- 在
web.xml
中加入1
2
3
4
5
6
7
8
9<!-- 配置監聽器 -->
<listener>
<listener-class>com.example.demo.Listener</listener-class>
</listener>
<!-- 配置ServletContext的參數,用來在初始化ServletContext的時候解耦 -->
<context-param>
<param-name>Servlet參數名稱</param-name>
<param-value>資源文件的路徑</param-value>
</context-param> - 實現監聽器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void contextInitialized(ServletContextEvent sce) {
// 加載資源文件
//1. 獲取ServletContext對象
ServletContext context = sce.getServletContext();
//2. 加載資源文件
String configurationLocation = context.getInitParameter("Servlet參數名稱");
//3. 獲取真實路徑
String path = context.getRealPath(configurationLocation);
//4. 加載資源文件到内存中
try {
FileInputStream fileStream = new FileInputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
- 在
- JQuery
- 定義:Jquery是一個快速,簡潔的JavaScript框架,簡化了JavaScript開發
- JavaScript框架本質上就一個JavaScript文件,裏面封裝了JavaScript的原生代碼而已
- 在html中使用JQuery
- 下載JQuery文件:
jquery-xxx.js
有可閲讀性,jquery-xxx.min.js
是压缩過的文件,文件體積小,加載速度快,生產環境用這個 - 通過script導入jQuery文件
- 一個簡單例子
1
2
3
4
5
6// 使用原生JavaScript
let div1 = document.getElementById("div1");
alert(div1.innerHTML);
// 使用JQuery
let div2 = $("#div2);
alert(div2.html());
- 下載JQuery文件:
- JQuery對象和JavaScript對象
- jq對象在操作的時候更方便
- jq對象和js對象的方法不通用,需要進行互相轉換
- 把jq對象轉換成js對象:
$(jq對象).get(索引)
或者$(jq對象)[索引]
- 把js對象轉換成jq對象:
$(js對象)
- jq的一些基本語法
- 事件綁定:
1
2
3
4
5
6
7
8
9
10// 原生js的寫法
let div1 = document.getElementById("div1")
div1.onclick = () => {
alert(div1.innerHTML);
}
// jq的寫法
let div2 = $("#div2");
div2.click(() => {
alert(div2.html());
}) - 入口函數:
頁面加載完成之後執行,如果有多個
window.onload
只執行最後一個,如果有多個$()
則按照次序執行1
2
3
4
5
6
7
8
9
10
11// 原生js的寫法
window.onload = () => {
let d1 = document.getElementById("div1")
alert(d1.innerHTML);
}
//jq的寫法
$(() => {
let d2 = $("#div2");
alert(d2.html());
}); - 樣式控制
1
2
3
4
5
6// 原生js的寫法
let div1 = document.getElementById("div1");
div1.style.backgroundColor = "red";
// jq的寫法
let div2 = $("#div2");
div2.css("backgroundColor", "red");
- 事件綁定:
- jq的選擇器
- 基本選擇器:
$("html標簽")
,$("#id屬性值")
.$(".class屬性值")
- 層級選擇器:
$("A B")
(後代選擇器: 選擇A元素内部的所有B元素),$(A > B)
(選擇A元素内部的所有B子元素) - 屬性選擇器:
$("對象名[屬性名]")
(包含指定屬性的選擇器),$("對象名[屬性名='屬性值']")
(包含指定屬性等於指定屬性值的選擇器),$("對象名[屬性名1='屬性值1'][屬性名2='屬性值2']")
(包含多個屬性條件的選擇器 - 過略選擇器:
:fist
,:last
,:not(篩選條件)
,:even
,:odd
,:eq(索引)
,:gt(索引)
,:lt(索引)
,:header
(篩選h1-h6) - 表達過濾選擇器:
:enabled
,:disabled
,:checked
,:selected
- 基本選擇器:
- DOM操作
- 内容操作:
html()
(獲取/設置元素的標簽躰内容),text()
(獲取/設置元素的標簽躰的純文本内容),val()
(獲取/設置元素的value屬性值) - 通用屬性操作:
attr()
(獲取/設置元素的屬性),removeAttr()
(刪除屬性),prop()
(獲取/設置元素的屬性),removeProp()
(刪除屬性)- 如果操作元素的固有屬性,用
prop
,如果操作元素的茲定於屬性,使用attr
- 注意
checked
,selected
,disabled
等屬性只能使用prop
,用attr
結果是undefined
- 對class屬性的操作:
addClass()
(添加class屬性),removeClass()
(刪除class屬性),toggleClass()
(切換class屬性, 如果存在class屬性就刪除,如果不存在class屬性就刪除) - CRUD相關操作
A.append(B)
: 元素A將元素B添加到元素A的最後一個子元素A.prepend(B)
: 元素A將元素B添加到元素A的第一個子元素A.appendTo(B)
:元素B將元素A添加到元素B的最後一個子元素A.prependTo(B)
: 元素B將元素A添加到元素B的第一個子元素A.after(B)
: 將元素B添加到元素A的後面A.before(B)
: 將元素B添加到元素A的前面A.insertAfter(B)
: 將元素A添加到元素B的後面A.insertBefore(B)
: 將元素A添加到元素B的前面A.remove()
: 刪除元素AA.empty()
: 刪除元素A的所有子元素
- 内容操作:
- jq的動畫效果:
show()
,hide()
,toggle()
,slideDown()
,slideUp()
,slideToggle()
,fadeIn()
,fadeOut()
,fadeToggle()
- 遍歷方式
1
2
3
4
5
6<ul id="city">
<li id="1">東京</li>
<li id="2">南京</li>
<li id="3">西京</li>
<li id="4">北京</li>
</ul>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40let items = document.getElementById("city").getElementsByTagName("li");
// 原生js的for循環遍歷
for (let i = 0; i < items.length; i++) {
alert(items[i].innerHTML);
if (items[i].id == "3") {
break;
}
}
// 原生js的for-of循環遍歷
for(let item of items) {
alert(item.innerHTML);
if (item.id == "3") {
break;
}
}
// jq的each遍歷方式
let $items = $("#city li");
$items.each((index, item) => {
alert($(item).html());
// alert(item.innerHTML);
if (item.id == "3") {
return false;
}
});
//jq的$.each遍歷方式
$.each(cities, (index, item) => {
alert($(item).html());
if (item.id == "3") {
return false;
}
})
// jq的for-of遍歷方式
for (let city of cities) {
alert($(city).html())
if (city.id === "3") {
break;
}
} - 綁定事件方法
- 標準綁定方法:
jq對象.事件名稱(回調函數)
- on綁定事件/off解綁事件:
jq對象.on(事件名稱, 回調函數)
,jq對象.off(事件名稱, 回調函數)
- 標準綁定方法:
- AJAX
- 異步:客戶端不需要等待服務器響應,在服務器處理請求的過程中,客戶端可以進行其他的操作
- Ajax是一種無需重新加載整個網頁的基礎上,能個更新部分網頁的技術
- 實現方式
- 使用原生的js代碼, 這個過於複雜,基本沒人用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
// 發送異步請求
const func = () => {
// 1.創建核心對象
let xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// 2. 建立連接
// xhttp.open(methodTypeString, urlString, booleanAjax)
// 發送一個異步請求到ajaxServlet?username=tom
xhttp.open("GET", "ajaxServlet?username=tom", true);
// 3. 發送請求
// get請求的參數在open的url中,這裏不用傳參
// post請求在這裏寫 xhttp.send("key=value")
xhttp.send();
// 4. 處理服務器響應結果
// 黨請求狀態變化的時候觸發事件
xhttp.onreadystatechange = function () {
// 如果響應完畢,并且是響應成功的話
if (this.readyState == 4 && this.status == 200) {
document.getElementById("resp").innerHTML = xhttp.responseText;
}
};
}
</script>
</head>
<body>
<input type="button" value="發送異步請求" onclick="func();">
<input>
<!-- 展示響應的結果 -->
<div id="resp"></div>
</body>
</html> - 使用jq實現:
$.ajax()
,$.get()
,$.post()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 使用$.ajax()發送請求
$.ajax({
url: "ajaxServlet", // 請求路徑
type: "GET", // 請求方式
// 請求參數有兩種寫法
// 第一種請求參數用字符串表示 data: "k1=v1&kn=vn"
// 第二種請求參數用JSON表示
data: {
"username": "tom"
},
success: (resp) => {
$("#resp").html(resp);
}, // 請求成功的回調函數
error: () => {
alert("發生錯誤了");
}
}) - 使用jq的$.get()發送請求:
get()
和post()
流程完全一致1
2
3
4
5
6
7
8
9$.get("ajaxServlet", { // 本體裏面只寫請求url和可選的請求參數
"username": "tom"
}).done((resp) => { // done在jq3.0之後取代了success, 裏面寫請求成功的回調函數
$("#resp").html(resp);
}).fail(() => { // fail在jq3.0之後取代了error, 裏面寫請求失敗的回調函數
alert("請求失敗了")
}).always(() => { // always在jq3.0之後取代了complete, 裏面寫請求完畢的回調函數
console.log("請求完畢")
});
- 使用原生的js代碼, 這個過於複雜,基本沒人用
- JSON
- JSON現在多用於存儲和交換信息的語法,類似XML,但是更小,更快,更容易解析
- JSON的key可以不寫引號,可以用單引號,可以用雙引號
- JSON基本語法
- 獲取JSON的值:
json.keyName
, 或者json["keyName"]
- 使用
for-in
循環遍歷JSON,注意這個循環中key是字符串,需要用json[key]
來取值
- 獲取JSON的值:
- JSON數據和Java對象的轉換
- 服務器内部是Java對象,在服務器和客戶端之間傳輸是JSON字符串,客戶端是JSON
Object(切記客戶端需要
resp = JSON.parse(resp)
把JSON字符串轉換成JSON Object) - JSON解析器:
Jsonlib
,Gson
,fastjson
,jackson
- 使用Jackson把JSON轉換成Java對象
- 步驟
- 導入jar包:
jackson-annotations.jar
,jackson-core.jar
,jackson-databind.jar
- 創建Jackson核心對象
ObjectMapper
- 調用ObjectMapper的方法把JSON字符串轉換成Java對象:
om.readValue(json, Class<T>)
把JSON字符串轉換成Java的T類型對象
- 導入jar包:
- 步驟
- 使用Jackson把Java對象轉換成JSON
- 步驟
- 導入jar包:
jackson-annotations.jar
,jackson-core.jar
,jackson-databind.jar
- 創建Jackson核心對象
ObjectMapper
- 調用ObjectMapper的方法把Java對象轉換成JSON:
om.writeValue(File/Writer/OutputStream, Java對象)
把Java對象轉換成JSON字符串,然後寫到文件/字符流/字節流中,om.writeValueAsString(Java對象)
把Java對象轉換成JSON字符串
- 導入jar包:
- 使用注解定制化轉換
@JsonIgnore
: 忽略Java對象的某個屬性不進入轉換完的JSON字符串@JsonFormat(pattern="定制化格式")
:自定義Java對象的某個屬性轉換成JSON字符串的key的格式
- 步驟
- 服務器内部是Java對象,在服務器和客戶端之間傳輸是JSON字符串,客戶端是JSON
Object(切記客戶端需要
- Redis
- 概念: Redis是一款高性能的NoSQL系列的非關係型數據庫,它是一個key-value的數據庫,存儲在内存中,查詢速度快
- Redis在windows下使用
- 按照官网的指示,在WSL中安装使用
- 纯粹的Windows版本:
https://github.com/tporadowski/redis/releases
下载即可使用
- Redis的数据结构
- Redis存储的是k-v格式的数据,其中key是字符串,value有5种不同的数据结构
- Value数据類型:字符串
string
, 哈希類型hash
(map), 列表類型list
(linked list), 集合類型set
, 有序集合類型sortedset
- Value数据類型:字符串
- 字符串類型:
set key val
,get key
,del key
(刪除完之後nil
) - 哈希類型:
hset key field val
,hget key field
(獲取指定的值),hgetall key
(獲取所有的key和value),hdel key field
- 列表類型:
lpush key val
,rpush key val
,lrange key start end
(獲取指定範圍的所有元素,lrange key 0 -1
獲取列表中所有的值),lpop key
,rpop key
- 集合類型:
sadd key val
,smembers key
(獲取set中所有元素),srem key val
- 有序集合:
zadd key score
(按照分數從小到大排序),zrange key start end
(獲取有序集合中範圍内的元素),zrange key start end with scores
(顯示有序集合中指定範圍的元素和對應的score),zrem key val
- Redis存储的是k-v格式的数据,其中key是字符串,value有5种不同的数据结构
- 其他常用語法:
keys *
: 查詢所有key,type key
: 顯示對應value的類型,del key
- 持久化機制
- redis是一個内存數據庫,當redis服務器重啓,數據會丟失,可以將redis内存中的數據持久化保存到硬盤的文件中
- 持久化機制
- RDB:
默認持久化機制,不需要進行配置。在一定的時間間隔中,檢查key的變化,然後持久化數據
- 在
redis.windows.conf
中有設置持久化要求,可以修改來自定義配置1
2
3
4
5
6# after 900 sec (15 min) if at least 1 key changed
save 900 1
# after 300 sec (5 min) if at least 10 keys changed
save 300 10
# after 60 sec if at least 10000 keys changed
save 60 10000 - 默認持久化到
dump.rdb
文件中,服務器啓動會讀取這個文件裏的數據
- 在
- AOF:
使用日志記錄的方式,記錄每一條命令的操作。可以每一次操作後都持久化到硬盤
- 在
redis.windows.conf
中把appendonly no
(關閉AOF)改成appendonly yes
(開啓AOF) - 在
redis.windows.conf
中有下面的配置1
2
3# appendfsync always 每個操作都持久化,默認關閉
appendfsync everysec # 每秒持久化一次,默認開啓
# appendfsync no 不持久化,默認關閉
- 在
- RDB:
默認持久化機制,不需要進行配置。在一定的時間間隔中,檢查key的變化,然後持久化數據
- Jedis
- 定義: Jedis是一款Java操作redis數據庫的工具
- 使用Jedis:導入
jedis.jar
,gson.jar
(jedis依賴這個),commons-pool2.jar
(jedis官方推薦使用連接池)1
2
3
4
5
6
7
8// 不使用連接池,直接使用jedis
Jedis jedis = new Jedis("url", port);
jedis.set("key", "value");
jeidis.close();
//使用連接池
JedisPooled jedis = new JedisPooled("url", port);
jedis.set("key", "value");
jedis.close(); - Jedis操作redis的方法和redis原生方法的名字完全一致,還有一些其他方法:
jedis.setex(key, sec, val)
在經過sec秒之後,key將被刪除
- 用Redis對數據庫進行緩存優化
- 先查找redis,如果有數據則直接讀取内存中數據
- 如果沒有數據,則查找數據庫數據,并且存儲到緩存中
- 注意如果發生了對數據庫的增刪改操作,需要清空redis緩存
- Maven
- Maven是一個項目管理工具,包含了一個項目對象模型(POM: Project Object Model,把一個項目看作一個POM), 一組標準集合,一個項目生命周期,一個依賴管理系統,和用來運行定義在生命周期階段中插件目標的邏輯
- Maven解決的問題
- 沒有Maven,需要手動導入jar包,Maven可以管理jar包
- Maven可以管理項目的生命周期,例如:编译、打包、部署
- Maven環境配置
- 下載Maven,把
maven路徑/bin
配置到環境變量裏面從而可以直接使用mvn
命令 - 在命令行中輸入
mvn -v
,如果顯示maven版本信息,則表示Maven環境配置成功 - 倉庫分類:本地倉庫,遠程倉庫,中央倉庫
- 修改Maven的默認本地倉庫:
修改
maven目錄/conf/settings.xml
文件1
2
3<settings>
<localRepository>路徑</localRepository>
</settings> - 中國大陸訪問中央倉庫較慢,可以設置阿里雲的鏡像:
把下面内容添加到
maven目錄/conf/settings.xml
文件的<mirrors></mirrors>
標簽中1
2
3
4
5
6<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror> - 在Idea新建Maven項目的時候選擇正確的倉庫路徑和settings.xml文件路徑
- 下載Maven,把
- Maven常用命令
- Maven項目結構
- 項目根目錄
- src目錄
- main目錄
- java目錄
- resources目錄
- webapp目錄: web項目才有,java項目沒有(存放jsp/html/WEN-INFO目錄)
- test目錄
- java目錄
- resources目錄
- main目錄
- target目錄:編譯完的class文件
- pom.xml文件
- Maven命令需要在項目根目錄下面執行
mvn compile
:編譯項目目錄/src/main/java
目錄下的所有java文件,得到的class文件存放在項目目錄/target/classes
目錄下mvn clean
:刪除項目目錄/target
目錄mvn package
:編譯java文件,并且把web項目打成war包,war包存放在項目目錄/target
目錄下mvn install
:把當前項目編譯打包,并且加入到本地maven倉庫中
- Maven項目結構
- Maven項目的生命周期
- Maven對項目構建分成三套相互獨立的生命周期
- Clean Lifecycle:在進行真正的構建項目之前進行清理工作
- Default Lifecycle:構建項目的核心部分,編譯,測試,打包,部署等
- Site Lifecycle:生成項目報告,站點,發佈站點
- 在同一個生命周期之中,執行後面的操作會自動執行前面的操作
- Maven對項目構建分成三套相互獨立的生命周期
- Maven坐標
- 作用:坐標用來唯一識別被maven管理的資源
- 坐標構成:
groupId
,artifactId
,version
,packaging
(jar適用於java項目/war適用於web項目/pom),scope
- 導入jar包的依賴:
在pom.xml的
<dependencies></dependencies>
坐標内加入<dependency></dependency>
標簽内部加入jar包的坐標 - 導入編譯插件:
在pom.xml的
<build><plugins></plugins></build>
坐標内加入<build></build>
標簽内部加入jar包的坐標
- 依賴範圍
- 依賴範圍的取值:
compile
: 對於編譯環境(java目錄下面可以用),測試環境(test目錄下面可以用),運行環境(打包完的壓縮包裏面有)都有效test
:對於測試環境有效provided
:對於編譯環境,測試環境有效runtime
:對於測試環境和運行環境有效system
:對於編譯環境和測試員警有效
- 依賴範圍的取值: