1、分層結(jié)構(gòu)
目的:實現(xiàn)應(yīng)用程序的模塊化和解耦,便于團隊協(xié)作和維護。
?1. 控制層(Controller):控制層主要負責處理用戶請求和響應(yīng)
?2. 服務(wù)層(Service):服務(wù)層主要負責業(yè)務(wù)邏輯的處理。
?3. 數(shù)據(jù)訪問層(DAO):數(shù)據(jù)訪問層主要負責與數(shù)據(jù)庫進行交互。

2、使用技術(shù)

2.1、 控制層(Controller):
Servlet 是 Java Web 應(yīng)用程序的基礎(chǔ),它提供了一種用于處理 HTTP 請求的標準 Java 接口,每個 Servlet 類處理特定的請求。在早期的 Web 開發(fā)中,Servlet 是一種主流的解決方案。
2.1.1、 Servlet的工作模式
客戶端發(fā)送請求至服務(wù)器
服務(wù)器運行并調(diào)用Servlet,Servlet根據(jù)客戶端請求生成響應(yīng)內(nèi)容并將其傳給服務(wù)器,響應(yīng)內(nèi)容動態(tài)生成,通常取決于客戶端的請求
服務(wù)器將響應(yīng)返回客戶端
出自CSDN博主「atCarl」的原創(chuàng)文章,原文鏈接:https://blog.csdn.net/2201_75955594/article/details/130232573
2.1.2、 Servlet的生命周期
Servlet的生命周期是由容器管理的,Servlet容器(例如Tomcat)會根據(jù)下面的規(guī)則來調(diào)用這三個方法:
初始化方法init( ),只會執(zhí)行一次(啟動Tomcat的時候默認是不執(zhí)行的,在訪問的時候才會執(zhí)行)當Servlet第一次被請求時,Servlet容器會實例化這個Servlet,然后就會調(diào)用這個方法來初始化Servlet,但是這個方法在后續(xù)請求中不會在被Servlet容器調(diào)用,我們可以利用init()方法來執(zhí)行相應(yīng)的初始化工作。
服務(wù)方法service( ),每當請求Servlet時,Servlet容器就會調(diào)用這個方法。第一次請求時,Servlet容器會先調(diào)用init( )方法初始化一個Servlet對象出來,然后會調(diào)用它的service( )方法進行工作,但在后續(xù)的請求中,Servlet容器只會調(diào)用service方法了。
service()方法根據(jù)請求的類型(Get、Post等)調(diào)用相應(yīng)的doGet()、doPost()等方法
要么重寫doGet、doPost ,要么重寫 service,必須二選一,而且必須進行重寫。銷毀方法destory(),當要銷毀Servlet時,Servlet容器就會調(diào)用這個方法,卸載應(yīng)用程序或者關(guān)閉Servlet容器時,就會發(fā)生這種情況,一般在這個方法中會寫一些清除代碼。
參考CSDN博主「atCarl」的原創(chuàng)文章,原文鏈接:https://blog.csdn.net/2201_75955594/article/details/130232573
2.1.3、 Servlet的開發(fā)步驟
1.導入 jar 包【servlet】
<!--tomcat依賴 內(nèi)含tomcat-servlet-api-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-api</artifactId>
<version>8.5.41</version>
<scope>provided</scope>
</dependency>
或者
<!--servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
2.創(chuàng)建一個 Servlet類

3.在web容器中注冊 servlet【配置Servlet】
一般有兩種方式:web.xml / 注解(Servlet 3.0 開發(fā)人員可以使用注解的方式配置Servlet)

4.寫一個 jsp 頁面(此處忽略)
5.處理邏輯并響應(yīng)請求

參考知乎作者[JasonWX]的原創(chuàng)文章,原文鏈接:https://zhuanlan.zhihu.com/p/591105720?utm_id=0
2.1.4、 Servlet之Filter(過濾器)
Filter(過濾器):過濾器可以動態(tài)地攔截請求和響應(yīng),以變換或使用包含在請求或響應(yīng)中的信息,也就是說可以監(jiān)視,修改或以某種方式處理客戶端與服務(wù)端下在交流的數(shù)據(jù)。

(1)在客戶端的請求訪問后端資源之前,攔截這些請求。
例如靜態(tài)資源或登錄、注冊、驗證碼等資源放行,其他資源攔截(2)在服務(wù)器的響應(yīng)發(fā)送回客戶端之前,處理這些響應(yīng)。
亂碼的統(tǒng)一處理、過濾非法字符、對非法的請求進行攔截(權(quán)限校驗)
過濾器的生命周期:
1)服務(wù)器啟動時實例化過濾器
2)調(diào)用init()方法初始化參數(shù)
3)利用FilterConfig ->getInitParams(name)方法獲取參數(shù)值
4)客戶端請求數(shù)據(jù)時doFilter()執(zhí)行過濾器
5)服務(wù)器關(guān)閉時容器銷毀過濾器實例前調(diào)用destory()方法
過濾器的開發(fā)步驟
1)創(chuàng)建一個類實現(xiàn)javax.servlet.Filter接口
2)重寫接口中所有的方法,其中有doFilter方法,用來執(zhí)行過濾的任務(wù)。有請求和響應(yīng)兩個參數(shù)
3)注冊過濾器在 web.xml中進行配置 或者 通過注解的方式:@WebFilter("過濾的地址")

2.1.5、 Servlet之Listener(監(jiān)聽器)
Listener(監(jiān)聽器):
用于監(jiān)聽web應(yīng)用中某些對象、信息的創(chuàng)建、銷毀、增加,修改,刪除等動作的發(fā)生,然后作出相應(yīng)的響應(yīng)處理。當范圍對象的狀態(tài)發(fā)生變化的時候,服務(wù)器自動調(diào)用監(jiān)聽器對象中的方法。常用于統(tǒng)計在線人數(shù)和在線用戶,系統(tǒng)加載時進行信息初始化,統(tǒng)計網(wǎng)站的訪問量等等。
特點:
1)監(jiān)聽方法中的邏輯代碼由程序員根據(jù)需要編寫
2)監(jiān)聽方法由tomcat根據(jù)監(jiān)聽結(jié)果來調(diào)用執(zhí)行
相關(guān)概念:
事件源:被監(jiān)聽的對象(三個域?qū)ο?request、session、servletContext)
監(jiān)聽器:監(jiān)聽事件源對象事件源對象的狀態(tài)的變化都會觸發(fā)監(jiān)聽器
注冊監(jiān)聽器:將監(jiān)聽器與事件源進行綁定
響應(yīng)行為:監(jiān)聽器監(jiān)聽到事件源的狀態(tài)變化時所涉及的功能代碼(程序員編寫代碼)
監(jiān)聽器的類別:
在JavaWEB中,監(jiān)聽器分為三大類:
| 對象類型 | 作用范圍 | 生命周期 |
|---|---|---|
| ServletContext (應(yīng)用域、上下文域) | 所有用戶的所有請求 | 服務(wù)器關(guān)閉時結(jié)束 |
| HttpSession(會話域) | session銷毀之前 | 默認30分鐘 |
| HttpServletRequest(請求域) | 一個用戶的一次請求 | 每次請求就結(jié)束 |
八大種:
1)監(jiān)聽域?qū)ο?/strong>需要在web.xml中配置
| 對象類型 | 對應(yīng)的監(jiān)聽器 | 作用 |
|---|---|---|
| ServletContext | ServletContextListener | 監(jiān)聽ServletContext的生命周期 |
| HttpSession | HttpSessionListener | 監(jiān)聽Session的生命周期 |
| HttpServletRequest | ServletRequestListener | 監(jiān)聽Request的生命周期 |
2)監(jiān)聽屬性需要在web.xml中配置
| 對象類型 | 對應(yīng)的監(jiān)聽器 | 作用 |
|---|---|---|
| ServletContext | ServletContextAttributeListener | 監(jiān)聽ServletContext屬性的變化 |
| HttpSession | HttpSessionAttributeListener | 監(jiān)聽Session屬性的變化 |
| HttpServletRequest | ServletRequestAttributeListener | 監(jiān)聽Request屬性的變化 |
3)監(jiān)聽session對象狀態(tài)不需要在web.xml中配置
| 對象類型 | 對應(yīng)的監(jiān)聽器 | 作用 |
|---|---|---|
| HttpSession | HttpSessionBindingListener(綁定,解除綁定) | 監(jiān)聽session的屬性綁定與移除 |
| HttpSession | HttpSessionActivationListener(鈍化和活化) | 監(jiān)聽session中的屬性鈍化與活化 |
(一)鈍化:當服務(wù)器正常關(guān)閉時,還存活著的session(在設(shè)置時間內(nèi)沒有銷毀) 會隨著服務(wù)器的關(guān)閉被以文件(“SESSIONS.ser”)的形式存儲在tomcat 的work 目錄下,這個過程叫做Session 的鈍化。
(二)活化:當服務(wù)器再次正常開啟時,服務(wù)器會找到之前的“SESSIONS.ser” 文件,從中恢復之前保存起來的Session 對象,這個過程叫做Session的活化。
監(jiān)聽器的開發(fā)步驟
1)實現(xiàn)對應(yīng)的接口
2)重寫接口中的方法
3)在web.xml中注冊該listener(根據(jù)監(jiān)聽器類型判斷需不需要)
參考CSDN博主「猿小許」的原創(chuàng)文章,原文鏈接:https://blog.csdn.net/weixin_44205087/article/details/117923765。
2.1.6、 請求轉(zhuǎn)發(fā)和請求重定向
轉(zhuǎn)發(fā)與重定向?qū)崿F(xiàn)的功能是相同的,用于從一個頁面跳轉(zhuǎn)到另一個頁面。但是從內(nèi)部細節(jié)上有很大的不同。
請求轉(zhuǎn)發(fā)(forward):發(fā)生在服務(wù)端程序內(nèi)部,由服務(wù)器進行的頁面跳轉(zhuǎn),因此也叫服務(wù)器內(nèi)部轉(zhuǎn)發(fā);當服務(wù)器端收到一個客戶端的請求之后,會先將請求轉(zhuǎn)發(fā)給目標地址,再將目標地址返回的結(jié)果轉(zhuǎn)發(fā)給客戶端, 而客戶端對于這一切毫無感知的。
轉(zhuǎn)發(fā)的特點:
1)轉(zhuǎn)發(fā)是服務(wù)器內(nèi)部的行為,因此在轉(zhuǎn)發(fā)時,客戶端請求的地址不會發(fā)生變化,即使服務(wù)器內(nèi)部已經(jīng)跳轉(zhuǎn)了好幾次了,但是客戶端訪問的地址依舊不變;
2)整個轉(zhuǎn)發(fā)過程中,客戶端從始至終只發(fā)送了一次請求;
3)由于在整個轉(zhuǎn)發(fā)過程中,客戶端只發(fā)送了一次請求,因此請求域的數(shù)據(jù)不會失效;
4)轉(zhuǎn)發(fā)是服務(wù)器內(nèi)部的行為,因此我們不需要加上項目名;例如:request.getRequestDispatcher("/demo01")
轉(zhuǎn)發(fā)示例:客戶端訪問資源/demo01,服務(wù)器發(fā)現(xiàn)客戶端想要的資源不在/demo01于是自己將請求轉(zhuǎn)發(fā)到/demo02,這個過程客戶端無感知;客戶端只請求了一次;

請求重定向:服務(wù)器端接收到客戶端的請求之后,會給客戶端返回了一個臨時響應(yīng)頭,這個臨時響應(yīng)頭中記錄了客戶端需要再次發(fā)送請求(重定向)的 URL 地址,客戶端根據(jù)服務(wù)器反饋的信息再次請求服務(wù)器,這就是請求重定向。
重定向的特點:
1)由于客戶端重定向是客戶端再次請求,因此客戶端的地址欄的地址已經(jīng)發(fā)生了變化;
2)客戶端請求了兩次服務(wù)器
3)在整個重定向過程中,客戶端發(fā)送了兩次請求,請求域中的數(shù)據(jù)丟失;
4)重定向是客戶端行為(客戶端再次請求),我們需要加上項目名;例如:sendRedirect("request.getContextPath()+/demo01")
如果需要保留請求域中的數(shù)據(jù),使用轉(zhuǎn)發(fā),否則使用重定向。
重定向示例:客戶端首先訪問資源/demo01,服務(wù)器發(fā)現(xiàn)想要的資源不在demo01這里,于是告訴(sendRedirect)客戶端:"你要的資源不在我這里,你去訪問/demo02"吧!于是客戶端再次訪問資源/demo02;在重定向中,客戶端已經(jīng)請求了兩次服務(wù)器。

2.2、服務(wù)層(Service):
開發(fā)人員主動管理對象。
2.3、 數(shù)據(jù)訪問層(DAO):
Java Database Connectivity,簡稱JDBC;是Java語言中用來規(guī)范客戶端程序如何來訪問數(shù)據(jù)庫的應(yīng)用程
序接口,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的方法。
2.3.1、 JDBC的優(yōu)點:
- JDBC API是一組接口,沒有具體的實現(xiàn)。實現(xiàn)類由各數(shù)據(jù)庫廠商去實現(xiàn)。只需要調(diào)用接口中
的方法即可。 - JDBC代碼只需要少量的修改就可以訪問另一種數(shù)據(jù)庫。
- 所有數(shù)據(jù)庫框架,底層最終都會轉(zhuǎn)成JDBC的代碼來實現(xiàn)。
2.3.2、 JDBC的操作步驟
2.3.2.1、 加載驅(qū)動
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
2.3.2.2、 獲得連接
//數(shù)據(jù)庫連接信息,一般配置在.properties文件中
String url = "jdbc:mysql://localhost:3306/project_db?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC";
String username= "root";
String password= "abc1234.";
//獲取連接
Connection con = DriverManager.getConnection(url, username, password);
2.3.2.3、 執(zhí)行指令
PreparedStatement ps = con.prepareStatement("select * from studernt");
2.3.2.4、 獲取查詢結(jié)果集(查詢才有,增刪無此步驟)
List<Student> list=new ArrayList<Student>();
rs=ps.executeQuery();
while (rs.next()) {
Student stu=new Student();
stu.setId(rs.getInt(1));
stu.setName(rs.getString(2));
stu.setAge(rs.getInt(3));
stu.setBirthday(rs.getDate(4));
list.add(stu);
}
2.3.2.5、 關(guān)閉資源
if(con!=null&&!con.isClosed()) {
con.close();
}
if(ps!=null) {
ps.close();
}
參考CSDN博主「LJT_1314520」的原創(chuàng)文章,原文鏈接:https://blog.csdn.net/LJT_1314520/article/details/125241649
2.3.3、 SQL注入問題
Statement是先在sql語句中傳值再編譯,存在sql注入問題。
PreparedStatement是先對sql語句進行預(yù)編譯,然后再傳入變量值,預(yù)編譯中的待傳參數(shù)部分用?占位符代替。解決了sql注入
2.3.4、 連接池
原因:每次訪問數(shù)據(jù)庫都必須先創(chuàng)建連接,執(zhí)行完畢以后關(guān)閉連接對象。連接對象需要不停的創(chuàng)建,不停的關(guān)
閉。
問題:
1)數(shù)據(jù)庫創(chuàng)建連接通常需要消耗相對較多的資源,創(chuàng)建時間也較長,而每次操作都要重新獲取新的
連接對象,執(zhí)行一次操作就把連接關(guān)閉,這樣數(shù)據(jù)庫連接對象的使用率低。
2)假設(shè)網(wǎng)站一天10萬訪問量,數(shù)據(jù)庫服務(wù)器就需要創(chuàng)建10萬次連接,極大的浪費數(shù)據(jù)庫的資源,并
且極易造成數(shù)據(jù)庫服務(wù)器內(nèi)存溢出。

| 連接對象 | 操作特點 |
|---|---|
| 創(chuàng)建時 | 程序啟動的時候,由應(yīng)用程序在內(nèi)存中創(chuàng)建好一定數(shù)量的連接對象,放在內(nèi)存中,這個內(nèi)存區(qū)域就稱為連接池,也叫數(shù)據(jù)源DataSource 。 |
| 使用時 | 訪問數(shù)據(jù)庫的時候,直接從連接池中得到一個連接對象就可以了。 |
| 關(guān)閉時 | 不是真的關(guān)閉連接,而是將連接對象再放回到連接池中,給下一個用戶使用。 |
作用:
1)提高獲取連接對象的速度;
2)提高連接對象的使用率,每個連接對象可以重復使用。