1. SpringBoot
1.1 web服務器
Spring Boot 的 web 服務器原理主要基于其嵌入式服務器的概念,這意味著它內嵌了一個 web 服務器,無需部署到外部服務器上。Spring Boot 內嵌了如 Tomcat、Jetty 或 Undertow 等 servlet 容器。
1.2 servlet
Servlet(Server Applet)是 Java EE(Java Enterprise Edition)規范的一部分,它是一個運行在服務器端的Java程序,用于生成動態網頁。Servlet 容器(如 Tomcat)負責管理 Servlet 的生命周期和執行。以下是 Servlet 編程的基本原理:
-
Servlet 接口:Servlet 必須實現
javax.servlet.Servlet
接口,該接口定義了五個方法:init()
,service()
,getServletConfig()
,getServletInfo()
, 和destroy()
。 -
Servlet 生命周期: Servlet 的生命周期從實例化開始,通過
init()
方法初始化,然后可以接收服務請求,最后通過destroy()
方法銷毀。 -
請求處理: Servlet 容器接收到客戶端的請求后,會調用 Servlet 的
service()
方法,該方法根據請求的類型(如 GET 或 POST)決定調用doGet()
或doPost()
方法。 -
多線程:Servlet 容器支持多線程,Servlet 容器可以創建多個 Servlet 實例或使用同一個 Servlet 實例的多個線程來處理并發請求。
-
Servlet 映射: Servlet 映射定義了 URL 到 Servlet 的映射關系。映射可以在 web.xml 文件中配置,也可以使用注解(如
@WebServlet
)。 -
請求和響應對象: Servlet 使用
HttpServletRequest
和HttpServletResponse
對象來與客戶端進行交互。HttpServletRequest
包含了請求信息,而HttpServletResponse
用于構造響應。 -
事件監聽器: Servlet API 提供了多種事件監聽器,如
ServletContextListener
、HttpSessionListener
等,它們可以在 Servlet 生命周期的關鍵時刻執行特定的操作。 -
過濾器: Servlet 容器允許開發者實現
javax.servlet.Filter
接口來攔截請求和響應,進行預處理或后處理。 -
會話管理: Servlet API 通過
HttpSession
對象支持會話管理,允許在多個頁面請求之間保持狀態。 -
安全: Servlet 容器和 Servlet 可以配置安全性,如使用聲明式和編程式安全控制對資源的訪問。
-
Servlet 3.0 異步處理:Servlet 3.0 引入了異步處理機制,允許 Servlet 方法異步執行,不阻塞容器線程,提高應用程序的并發處理能力。
-
依賴注入: Servlet 3.0 還支持依賴注入(DI),可以直接在 Servlet 中注入服務和管理 Bean。
Servlet 編程模型為構建基于 Java 的服務器端應用程序提供了一種簡單、一致和可擴展的方式。通過 Servlet,開發者可以生成動態內容、處理表單提交、與數據庫交互等。
1.3 不依賴框架可以怎么做?
不使用 Spring 框架,您仍然可以使用 Java 的標準庫 java.net
或 javax.servlet
來提供 HTTP 接口。以下是兩種常見的方法:
1.3.1 使用 java.net
包
Java 的 java.net
包提供了基本的網絡通信能力,可以用來創建簡單的 HTTP 服務器。以下是一個使用 ServerSocket
和 Socket
來處理 HTTP 請求的簡單示例:
import java.io.*;
import java.net.*;public class SimpleHttpServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);System.out.println("Server is listening on port 8080...");while (true) {Socket socket = serverSocket.accept();BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));String inputLine;while ((inputLine = in.readLine()) != null) {if (!inputLine.isEmpty()) {break;}}// 簡單的請求處理邏輯String response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!";out.write(response);out.flush();// 關閉連接in.close();out.close();socket.close();}}
}
這個示例創建了一個在 8080 端口上監聽的 HTTP 服務器,對于每個連接,它發送一個簡單的響應 “Hello, World!”。
1.3.2 使用 javax.servlet
包
如果您想使用更接近現代 Web 應用的方法,可以使用 javax.servlet
包來創建一個基于 Servlet 的 HTTP 服務器。以下是一個使用 HttpServlet
和 HttpServer
的示例:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType("text/plain");PrintWriter out = response.getWriter();out.println("Hello, World!");}public static void main(String[] args) throws Exception {HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);Context context = server.createContext("/myapp", (servletRequest, servletResponse) -> {new MyServlet().doGet(servletRequest, servletResponse);});server.start();System.out.println("Server is started at port 8080...");}
}
這個示例創建了一個 Servlet,它在接收到 GET 請求時返回 “Hello, World!”。然后使用 HttpServer
來啟動服務器,并定義了 URL 路徑 /myapp
來映射到我們的 Servlet。
請注意,這些示例都是非常簡單的基礎示例,用于演示如何不使用 Spring 框架來提供 HTTP 接口。在實際開發中,您可能需要考慮更多的因素,比如請求解析、路由、錯誤處理、安全性、并發處理等。對于更復雜的應用,您可能需要使用更高級的 Web 服務器或框架。
2. Gin
2.1 web服務器
Gin 是一個用 Go 語言編寫的 Web 框架,以其高性能和快速的開發效率而聞名。Gin 框架本身并不內嵌 Web 服務器,而是使用 Go 標準庫中的 net/http
包來處理 HTTP 請求和響應。net/http
包提供了一個非常輕量級的 HTTP 服務器,它足以應對大多數 Web 應用的需求。
以下是 Gin 框架啟動服務器的基本步驟:
-
創建路由: 使用 Gin 創建路由,定義應用程序的端點和對應的處理函數。
-
設置中間件: 可以設置中間件來處理跨域請求、日志記錄、請求驗證等。
-
啟動服務器: 使用
gin.Default()
或gin.New()
創建一個 Gin 實例,然后調用Run
方法來啟動服務器。Run
方法接受一個字符串參數,表示服務器監聽的地址和端口。 -
監聽端口: Gin 使用
http.ListenAndServe
函數來監聽指定的端口,等待客戶端的連接。 -
處理請求: 當客戶端發起請求時,Gin 會根據定義的路由規則將請求分發到對應的處理函數。
-
響應客戶端: 處理函數執行完成后,Gin 會構造 HTTP 響應并發送給客戶端。
下面是一個簡單的 Gin 應用啟動服務器的例子:
package mainimport ("github.com/gin-gonic/gin"
)func main() {router := gin.Default() // 創建一個默認的 Gin 路由器// 定義路由router.GET("/", func(c *gin.Context) {c.JSON(200, gin.H{
{ "message": "hello world"}})})// 啟動服務器,默認在 8080 端口監聽router.Run(":8080")
}
在這個例子中,Gin 應用定義了一個處理根 URL (/
) 的路由,當訪問這個路由時,它會返回一個 JSON 響應。然后,使用 router.Run(":8080")
啟動服務器,監聽 8080 端口。
2.2 不依賴框架如何提供HTTP接口?
在 Go 語言中,即使不使用 Gin 框架,也可以通過標準庫 net/http
來提供 HTTP 接口。以下是如何使用 Go 的 net/http
包來創建一個簡單的 HTTP 服務器并提供接口的示例:
package mainimport ("fmt""log""net/http"
)// 定義一個處理函數,它響應客戶端請求
func myHandler(w http.ResponseWriter, r *http.Request) {// 設置響應的頭部,指明返回的內容類型w.Header().Set("Content-Type", "text/plain")// 響應客戶端請求fmt.Fprintf(w, "Hello, World! This is a simple HTTP interface.")
}func main() {// 設置路由,將"/"路徑的請求映射到myHandler處理函數http.HandleFunc("/", myHandler)// 定義服務器監聽的端口port := "8080"// 啟動服務器,阻塞等待請求log.Printf("Server starting on port %s", port)if err := http.ListenAndServe(":"+port, nil); err != nil {log.Fatal("ListenAndServe error: ", err)}
}
在這個示例中,我們首先定義了一個 myHandler
函數,它接收 http.ResponseWriter
和 *http.Request
作為參數。http.ResponseWriter
用于構造 HTTP 響應,*http.Request
包含了請求的信息。
然后,我們使用 http.HandleFunc
來設置路由,將根路徑(“/”)的請求映射到 myHandler
函數。
最后,我們使用 http.ListenAndServe
來啟動服務器,它接受一個端口號和一個處理器(這里是 nil
,因為我們已經使用 HandleFunc
設置了處理器)。服務器將阻塞等待請求,并在接收到請求時調用相應的處理函數。
這個簡單的服務器將響應所有到根路徑的 GET 請求,并返回 “Hello, World! This is a simple HTTP interface.” 文本。
Go 的 net/http
包非常強大,它支持路由、中間件、文件服務、靜態文件處理等,可以滿足大多數 Web 服務的需求。對于更高級的用例,比如參數解析、JSON 響應、請求驗證等,你可能需要編寫更多的邏輯或使用第三方庫。
3. MyBatis
3.1 MyBatis 原理
MyBatis 是一個半自動的持久層框架,用于在 Java 應用程序中簡化數據庫操作。它提供了簡單的 API 和映射配置,以實現類型安全、對象關系映射(ORM)和 SQL 映射。以下是 MyBatis 的一些核心原理:
(1)SqlSessionFactory: SqlSessionFactory
是 MyBatis 的核心對象,負責創建 SqlSession
。它通過 XMLConfigBuilder
或 XMLMapperBuilder
加載配置文件和映射文件來構建。
(2)SqlSession:SqlSession
是 MyBatis 工作的主要執行者,提供了執行命令對象(如 update
, select
, delete
, insert
)的方法,以及提交和回滾事務的功能。
(3)映射文件:MyBatis 使用 XML 或注解來映射 SQL 語句和 Java 類。這些映射文件定義了 SQL 語句和 Java 對象之間的映射關系。
(4)動態 SQL:MyBatis 支持動態 SQL,允許使用 if、choose、when、otherwise、trim、set 等元素來構建條件 SQL。
(5)參數映射:MyBatis 支持自動將 Java 對象的屬性映射到 SQL 語句的參數上。
(6)結果映射:MyBatis 支持將查詢結果映射到 Java 對象。可以通過 <resultMap>
元素定義復雜的結果映射。
(7)一級緩存和二級緩存:
SqlSession
提供了一級緩存,它是會話級別的緩存,用于存儲查詢結果。- MyBatis 還支持二級緩存,它是全局的,可以被多個
SqlSession
共享。
(8)插件機制:MyBatis 允許開發者編寫插件來攔截方法的執行,實現自定義邏輯,如分頁、審計等。
(9)配置和映射的分離:MyBatis 支持將配置和映射分離,使得 SQL 映射可以獨立于配置文件存在。
(10)事務管理:MyBatis 支持聲明式事務和編程式事務管理。
(11)MyBatis Generator:MyBatis 提供了代碼生成器,可以根據數據庫表結構自動生成映射文件和 Java 模型類。
(12)MyBatis 3 增強的映射特性:MyBatis 3 引入了新的映射特性,如 @SelectProvider
, @UpdateProvider
, @InsertProvider
, 和 @DeleteProvider
注解,允許使用 Java 代碼提供 SQL 語句。
MyBatis 的設計哲學是提供足夠的靈活性,讓開發者可以編寫高效的 SQL 語句,同時保持代碼的簡潔性和可維護性。通過 MyBatis,開發者可以避免大量的樣板代碼,并能夠更精細地控制數據庫操作。
3.2 不使用框架該怎么做?
使用 Java 的 JDBC API 直接操作數據庫。JDBC 是 Java 語言中用來規范客戶端程序如何訪問數據庫的應用程序接口,通過 JDBC 可以連接任何符合 SQL 數據庫。
4. Gorm
4.1 Gorm原理
Gorm 是 Go 語言的一個對象關系映射(ORM)庫,用于操作各種 SQL 數據庫系統。它提供了一個簡單易用的 API 來與數據庫進行交互,同時隱藏了底層 SQL 語句的復雜性。以下是 Gorm 的一些核心原理:
(1)連接管理:Gorm 管理數據庫連接,允許配置多個數據源,并在它們之間進行切換。
(2)自動遷移:Gorm 支持自動遷移功能,可以自動創建或修改數據庫表結構以匹配 Go 結構體的定義。
(3)事務處理:提供了事務的支持,可以確保數據的一致性和完整性。
(4)關聯處理:支持定義模型之間的關聯關系,如一對一、一對多和多對多。
(5)預加載:支持預加載(Eager Loading)關聯數據,以減少數據庫查詢次數。
(6)懶加載:支持懶加載(Lazy Loading)關聯數據,按需從數據庫加載數據。
(7)條件查詢:支持鏈式調用,可以方便地構建復雜的查詢條件。
(8)批量操作:支持批量創建、更新和刪除記錄。
(9)事務回滾:在發生錯誤時,可以回滾事務,保證數據操作的原子性。
(10)自定義操作:允許自定義 SQL 語句或使用原生數據庫驅動的函數。
(11)日志記錄:提供了詳細的 SQL 日志記錄功能,方便調試。
(12)結果映射:將查詢結果映射到 Go 結構體中,簡化了結果處理。
(13)插件系統:Gorm 擁有豐富的插件系統,可以擴展其功能,如添加新的數據庫類型支持。
(14)接口和抽象:Gorm 提供了一系列接口和抽象,使得替換底層數據庫驅動或自定義行為變得簡單。
(15)兼容性:支持多種 SQL 數據庫,如 MySQL, PostgreSQL, SQLite, SQL Server 等。
Gorm 的工作原理基于以下幾個步驟:
- 定義模型:使用 Go 的結構體定義數據模型,Gorm 會根據這些結構體來生成和操作數據庫表。
- 連接數據庫:使用 Gorm 的
gorm.Open()
方法連接到數據庫。 - 創建/更新記錄:使用
Create()
,Save()
,Update()
等方法操作數據庫記錄。 - 查詢記錄:使用
Find()
,First()
,Scan()
等方法查詢數據庫。 - 刪除記錄:使用
Delete()
方法從數據庫中刪除記錄。 - 關閉數據庫連接:操作完成后,使用
Close()
方法關閉數據庫連接。
Gorm 通過簡化數據庫操作,讓開發者能夠更專注于業務邏輯,而不是底層的 SQL 語句。同時,它保留了足夠的靈活性,允許在需要時執行自定義的 SQL 操作。
4.2 不使用框架該怎么做?
如果不使用 Gorm 框架,你仍然可以使用 Go 語言的 database/sql
標準庫來直接與數據庫進行交互。以下是不使用 Gorm 框架實現數據庫操作的一些基本步驟:
(1)導入包:導入 database/sql
包以及其他可能需要的包,如 fmt
用于格式化輸出,log
用于記錄日志。
(2)配置數據源:使用正確的驅動和連接字符串配置數據源。
(3)創建數據庫連接:使用 sql.Open()
函數創建到數據庫的連接。
(4)準備 SQL 語句:使用 db.Prepare()
準備 SQL 語句,這有助于提高性能并防止 SQL 注入。
(5)執行 SQL 語句:使用 Exec()
, Query()
或 QueryRow()
執行 SQL 語句。
(6)處理結果:使用 Rows
或 Row
對象來迭代查詢結果。
(7)錯誤處理:檢查并處理可能發生的錯誤。
(8)關閉資源:使用 Close()
方法關閉 Rows
, Stmt
和 DB
對象以釋放資源。
(9)事務處理:使用 Tx
對象管理事務。
下面是一個簡單的示例,演示了如何使用 database/sql
包執行一個查詢并將結果打印出來:
package main
import ("database/sql""fmt""log"_ "github.com/go-sql-driver/mysql" // 導入 MySQL 驅動
)func main() {// 配置數據源dsn := "username:password@/dbname?charset=utf8&parseTime=True&loc=Local"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 測試連接err = db.Ping()if err != nil {log.Fatal(err)}// 準備 SQL 語句rows, err := db.Query("SELECT id, name FROM users")if err != nil {log.Fatal(err)}defer rows.Close()// 處理結果var id intvar name stringfor rows.Next() {err = rows.Scan(&id, &name)if err != nil {log.Fatal(err)}fmt.Printf("ID: %d, Name: %s\n", id, name)}// 檢查可能的錯誤err = rows.Err()if err != nil {log.Fatal(err)}
}
在這個示例中,我們首先導入了 MySQL 的驅動(需要使用 go get
命令安裝),然后配置了數據源字符串(DSN),接著創建了數據庫連接并測試了連接的有效性。之后,我們準備并執行了一個 SQL 查詢,迭代結果集并打印出每行的數據。最后,我們檢查并處理了可能發生的錯誤。
請注意,實際開發中還需要考慮 SQL 注入防護、更復雜的錯誤處理、日志記錄、配置和查詢參數化等因素。此外,你可能需要編寫額外的代碼來處理數據庫遷移、模型定義、關聯關系等高級功能。