學習一下 SpringCloud (一)-- 從單體架構到微服務架構、程式碼拆分(maven 聚合)
一、架構演變
1、系統架構、叢集、分散式系統 簡單理解
(1)什麼是系統架構?
【什麼是系統架構?】
系統架構 描述了 在應用程式內部,如何根據 業務、技術、靈活性、可擴充套件性、可維護性 等因素,將系統劃分成不同的部分並使這些部分相互分工、協作,從而提高系統的效能。
【簡單的理解:】
系統架構是 程式執行 的基石、其決定了程式是否能正確、有效的構建 以及 穩定的執行。
(2)叢集
【什麼是叢集?】 計算機叢集簡稱叢集,是一種計算機系統,它通過一組鬆散整合的計算機軟體或硬體連線起來、高度緊密地協作完成計算工作。 在某種意義上,他們可以被看作是一臺計算機。 集群系統中的單個計算機通常稱為節點,通常通過區域網連線(或者其它的連線方式)。 叢集計算機通常用來改進單個計算機的計算速度或可靠性。 【簡單的理解:】 通過多臺計算機完成同一個工作,達到更高的效率。 兩機或多機內容、工作過程等完全一樣。如果一臺宕機,另一臺可以起作用。 同一個業務,部署在多個伺服器上(不同的伺服器運行同樣的程式碼,幹同一件事)。
(3)分散式系統
【什麼是分散式系統?】
分散式系統是一組計算機,通過網路相互連線,傳遞訊息與通訊後,協調它們的行為而形成的系統。
元件之間彼此進行互動從而實現一個共同的目標。
【簡單的理解:】
通過多臺計算機相互作用完成一個工作,每個計算機負責一個功能模組,將多個計算機組合起來從而完成一個大任務。
一個業務拆分為多個子業務,部署在不同的伺服器上(不同的伺服器,執行不同的程式碼,為了同一個目的)。
注:
分散式與叢集不衝突,可以存在分散式叢集,即將一個專案拆分為不同的功能模組後,對各個不同的功能模組實現叢集。
(4)架構演變
Dubbo 官網將系統架構分為 單體架構、垂直架構、分散式服務架構、流計算架構。
可參考:
注:
下面的 系統演變過程 以此為 基礎 進行展開,有不對的地方還望不吝賜教。
2、系統架構需要考慮的因素(高可用、高併發、高效能)
(1)高可用性(High Availability)
【高可用性:】 高可用性 指的是 儘量縮短 維護操作(計劃內)以及 系統故障(非計劃內)而導致的停機時間,從而保證 系統 正常執行。 高可用性 具有高度 的容錯性、恢復性。 注: 容錯性 指的是 軟體發生故障時 仍能正常執行的 能力。 恢復性 指的是 軟體發生故障時 恢復到故障之前狀態的 能力。
(2)高併發(High Concurrency)
【高併發性:】 高併發性 指的是 保證系統能夠同時並行處理 很多請求。 通過 水平拓展 或者 垂直拓展的方式 可以提高系統高併發能力。 【垂直拓展:】 垂直拓展,提高當前系統的能力 以適應 需求。 又可分為兩種: 1、提升硬體能力,強化 伺服器硬體,比如:機械硬碟 更換為 固態硬碟,增加 CPU 核數、拓展系統記憶體 等。 2、提升軟體能力,優化 軟體效能,比如:使用快取 減少磁碟 I/O 次數,優化程式碼使用的資料結構 從而減少響應時間等。 注: 單機效能提升還是有限的,成本充足情況下,增加伺服器的使用還是比較合適的。 【水平拓展:】 水平拓展,也即 橫向拓展,增加系統個數 以適應 需求,只需要增加伺服器 的數量,就可以線性的增加系統的併發能力。 當然也不能一直增加伺服器,伺服器數量增多,維護、成本的壓力也就上來了。 【高併發相關指標:】 響應時間(Response Time):指的是 系統對 使用者輸入或者請求 進行處理並響應請求的時間。 吞吐量(Throughput):指的是 系統單位時間內 處理請求的數量(吞吐量一般與響應時間成反比,即吞吐量越大,響應時間越短)。 每秒查詢率(Query Per Second,QPS):指的是 系統 每秒響應的請求數。 併發使用者數:指的是 系統 某時刻支援 正常使用系統的使用者數。
(3)高效能(High Performance)
【高效能:】
高效能 指的是 程式處理速度快、佔用記憶體少、CPU 佔用率低。
也即系統性能強悍、運算能力強、響應時間短。
3、架構演變 -- 單體應用架構(傳統架構、三層架構、叢集)
(1)傳統架構
【背景:】
網際網路開發早期,所有的 業務、功能模組 程式碼都寫在一個專案中,然後 編譯、打包並部署到容器(比如:Tomcat)中執行。
此時稱為 All in One,即 所有模組程式碼均寫在一起(比如:Servlet、JSP 等程式碼)、技術上不分層。
【優點:】
所有功能均在同一個應用程式中,所以只需要部署 一個應用程式即可,減少了 部署節點、以及成本。
【缺點(出現的問題):】
程式碼可維護性差(所有程式碼均寫在一個專案中,沒有層次,相互呼叫複雜,不易修改)。
容錯性差(程式碼寫在 JSP 中,發生錯誤時 伺服器可能直接宕機 且 使用者可以直接看到 錯誤資訊)。
併發量小。
(2)三層架構
【背景:】
為了解決 傳統架構 可維護性差、容錯性差、併發量小 等問題,引入了 分層的概念。
分層 即 將程式碼 劃分出 幾個層級,每個層級 幹不同的事,從而提高程式碼的可維護性。
那麼如何分層呢?分幾層呢?
在開發早期,所有的邏輯程式碼沒有明顯的區分,程式碼間相互呼叫、職責不清,頁面邏輯、業務邏輯、資料庫訪問邏輯 等混合在一起,即一層架構,此時的程式碼維護、迭代工作肯定無比麻煩。
隨著時代的發展,資料庫訪問邏輯 被逐步的劃分出來,但頁面邏輯、業務邏輯 仍然混合在一起,即二層架構,此時簡化了資料訪問的操作,提高了系統的可維護性。
繼續發展,從功能、程式碼組織的角度進行劃分,將三種邏輯分開,也即三層架構出現。
【三層架構(MVC):】
從功能、程式碼組織的角度出發,按系統不同職責進行劃分成三個層次:
表示層:關注 資料顯示 以及 使用者互動。
業務邏輯層:關注 業務邏輯處理。
資料訪問層:關注 資料的儲存與訪問。
注:
此處的三層架構並非 物理分層,而是邏輯上的分層(所有程式碼仍然在一個專案中進行 開發、編譯、部署,仍是單體架構)。
【優點:】
提高了可維護性。每一層的功能具體化,解決了系統間 相互呼叫複雜、職責不清的問題,有效降低了層與層間的依賴關係,降低了維護、迭代的成本。
MVC 分層開發,提高了系統的容錯性。
伺服器分離部署。資料庫 以及 應用程式 可以部署在 不同的伺服器 上。
【缺點(出現的問題):】
併發量仍然不高(隨著使用者訪問量增加,單臺應用伺服器無法滿足需求)。
(3)叢集
【背景:】
為了解決 三層架構 的併發量問題,引入了 叢集的概念。
單臺伺服器不能滿足需求,那麼就使用 多臺 伺服器構成 叢集 提供服務。
【優點:】
使用 多臺伺服器 構成叢集 同時提供服務,提高了併發量。
提高了容錯性(一臺伺服器掛了,還有其他伺服器可以提供服務,保證程式的正常執行)。
【缺點(出現的問題):】
使用者的請求 傳送給 哪臺伺服器?
如何保證請求可以 平均的傳送給 各個伺服器?
資料如何進行共享、快取?
資料需要模糊查詢時,如何提高資料庫查詢效率?
資料庫訪問壓力如何解決?
資料量過大時,應該如何儲存?
【解決:】
可以通過 Nginx 解決 請求的傳送 以及 分發(負載均衡) 問題。
可以通過 Redis 解決 資料共享 以及 快取 問題。
可以通過 ElacticSearch 解決 資料搜尋 問題。
可以通過 MyCat 使用主從複製、讀寫分離,減輕資料庫壓力,通過分庫分表 的方式,按照指定的方式儲存資料。
(4)解決叢集出現的問題
【問題一:】 使用者 登入並訪問 伺服器時,會產生一個 session,且伴隨著使用者訪問的全過程 直至 使用者關閉瀏覽器結束此次訪問。 當一個伺服器突然宕機,若不對 session 進行處理,那麼其 session 必定丟失,也即 使用者需要重新 進行登入等 一系列操作,使用者體驗感將極差。 那麼 session 如何共享?也即 資料共享問題? 【解決:】 方式一: 使用 Tomcat 廣播 session,從而實現 session 共享。 每個 tomcat 會在區域網中廣播自己的 session 資訊並監聽其他 tomcat 廣播的 session 資訊,一旦 session 發生變化,其他的 tomcat 就能監聽並同步 session。 注: 此方式只適用於 併發量小的 小專案。 併發量大時,比如使用者量為 1000 萬,那每個伺服器都需要廣播、維護 session,那麼將會導致伺服器大量資源都用來處理 session,這樣肯定不適合大型專案。 方式二: 使用 Redis 儲存 session,從而實現 session 共享。 使用 Redis 儲存 session,當伺服器需要使用 session 時,直接從 Redis 中獲取,這樣只需要關心 Redis 的維護即可,減輕了 伺服器的壓力。 同時,Redis 還可以用來進行 資料快取,減少資料庫訪問次數(提高響應時間)。 注: 此方式適合於 大型的專案。 SpringBoot 整合 Redis 可參考:https://www.cnblogs.com/l-y-h/p/13163653.html#_label0
【問題二:】 使用叢集,即存在多個伺服器,一個使用者請求 肯定會被某一個伺服器進行處理,此時就需要考慮 伺服器 處理請求的問題了。 那麼 使用者的請求 傳送給 哪臺伺服器處理?如何保證請求可以 平均的傳送給 叢集中的各個伺服器? 【解決:】 可以通過 Nginx 解決 請求的傳送 以及 分發(負載均衡) 問題。 注: Nginx 反向代理、負載均衡等基本概念可參考:https://www.cnblogs.com/l-y-h/p/12844824.html#_label0
【問題三:】 通過上面介紹的 Nginx + Redis 的方式,提高了應用層的效能。 應用層問題解決了,資料庫的訪問壓力怎麼解決?如何提高資料庫的負載能力?資料庫資料量大時如何儲存? 【解決:】 採用讀寫分離、主從複製的方式,提高資料庫負載能力。 採用 master-slave 方式,master 負責 進行增刪改 等寫操作,slave 進行 讀操作,並通過主從複製的方式 將 master 資料同步到 slave 中。 設定多個 slave,從而提高資料庫查詢、負載能力。 採用分庫分表的方式,進行資料儲存。 注: 垂直拆分資料表:根據常用欄位 以及 不常用欄位 將資料表劃分為多個表,從而減少單個表的資料大小。 水平拆分資料表:根據時間、地區 或者 業務邏輯進行拆分。 垂直拆分仍有侷限性,水平拆分便於業務拓展。
【問題四:】
雖然進行了讀寫分離,資料量過大時,執行 模糊查詢 的效率低。對於大型的網站(電商等),搜尋是其核心模組,若一個查詢執行半天才能返回結果,那麼使用者體驗將是極差的。
那麼如何提高查詢的效率?
【解決:】
使用 搜尋引擎技術,比如:ElacticSearch、solr。
(5)單體架構總結
【單體架構:】
單體架構,就是將所有 業務、功能模組 都寫在一個專案中,編譯、打包並部署到容器(比如:Tomcat)中執行。
應用程式、資料庫 可以分開部署,可以通過部署 應用程式叢集、資料庫叢集 的方式提高系統性能。
能簡化 增刪改查 工作的 資料訪問框架(ORM) 也是提高系統性能的關鍵。
注:
隨著業務擴大、需求增加,單體架構將會變得臃腫、耦合性高,可維護性、可擴充套件性、靈活性都在逐步降低,
難以滿足業務快速迭代的需求,且成本不斷攀升,單體架構的時代已成為過去。
【優點:】
開發、測試、部署簡單,維護成本低。適用於 使用者、資料 規模小的 專案。
通過拓展叢集的方式 可以保證 高併發、高可用。
【缺點:】
可維護性、可拓展性差。(隨著業務增加、功能迭代,程式碼會變得臃腫、耦合)
技術棧受限,且只能通過 拓展叢集 的方式提高系統性能(維護成本高)。
協同開發不方便(存在修改相同業務程式碼的情況、導致衝突)。
4、架構演變 -- 垂直應用架構(水平拆分、垂直拆分)
(1)背景
上面介紹的 單體架構 已不能滿足實際場景(拓展能力有限、程式碼臃腫),那麼需要進行程式碼重構,對單體架構程式碼 進行拆分,那麼如何進行拆分 能保證 高可用、高併發、高效能 呢?
前面也提到了 高併發 可以通過 垂直拓展、水平拓展 來實現,此處不妨也對單體架構的程式碼 進行 水平拆分 以及 垂直拆分。
注:
拓展 與 拆分 是兩種概念。拓展是對外部改造,拆分是對內部改造。
垂直拓展:增加伺服器硬體效能,提高伺服器執行應用的能力。
水平拓展:增加伺服器數量,多節點部署應用。
垂直拆分:根據業務 對 系統進行劃分。
水平拆分:根據邏輯分層 對 系統進行劃分(比如:前後端分離、MVC 分層)。
(2)水平拆分
水平拆分 根據 邏輯分層 對系統進行劃分,將一個大的 單體應用程式 拆分成 多個小應用程式,每個小應用程式 作為 單獨的 jar 包,需要使用時,引入相關 jar 包即可。
【單體應用舉例:】
現有一個 SSM 單體應用,如何進行水平拆分?
【水平拆分思路:】
按照邏輯分層對程式碼進行拆分,將 controller、service、dao 層分別抽取出來,並打成 jar 包,需要使用時 直接引入 jar 包即可。
【使用 Maven 聚合的方式可以演示:】
Step1:構建一個 ssm_parent 工程(父工程,聚合下面的子工程,pom)
Step2:構建一個 ssm_bean 工程(子工程,存放 實體類,jar)
Step3:構建一個 ssm_mapper 工程(子工程,存放持久層類以及介面,jar)
Step4:構建一個 ssm_service 工程(子工程,存放 業務邏輯層類以及介面,jar)
Step5:構建一個 ssm_controller 工程(子工程,存放 控制層類,war)
也即目錄結構如下:
ssm_parent(pom)
ssm_bean(jar) 或者 ssm_pojo 名稱隨意取,見名知意 即可。
ssm_mapper(jar) 或者 ssm_dao
ssm_service(jar)
ssm_controller(war) 或者 ssm_web
注:
ssm_mapper 需要引入 ssm_bean.jar。
ssm_service 需要引入 ssm_mapper.jar。
ssm_controller 需要引入 ssm_service.jar。
可以通過 ssm_controller 或者 ssm_parent 啟動專案。
【水平拆分優點:】
模組可以複用,減少程式碼冗餘度。
程式碼分離部署,可以根據實際情況,增加 或者 減少 某些業務層的部署量。(比如:dao 層訪問量過大,可以多部署幾個 dao 服務減輕壓力。單體應用則是整體部署,增大了伺服器部署容量。)
【水平拆分缺點:】
各個模組業務仍然互動在一起,修改某個模組業務時,整個 jar 包(非修改模組)需要重新 測試 、部署,增加了 測試 與 維護 的壓力。
(3)垂直拆分:
垂直拆分 是 根據 業務 對系統進行劃分,將一個大的 單體應用程式 拆分成 若干個 互不相干的小應用程式(也即 垂直應用架構)。
每個小應用程式 就是一個單獨的 web 專案。
【單體應用舉例:】
現有一個 SSM 單體應用,如何進行垂直拆分?
【垂直拆分思路:】
按照業務對程式碼進行拆分,比如:現在 SSM 中存在 後臺管理業務、使用者業務、選單業務 等。
則將 這些業務 分別抽取出來,各自構成 web 工程。
【使用 Maven 聚合的方式可以演示:】
Step1:構建一個 ssm_parent 工程(父工程,聚合下面的子模組,pom)
Step2:構建一個 ssm_admin 模組(子模組,後臺管理模組)
Step3:構建一個 ssm_user 模組(子模組,使用者模組)
Step4:構建一個 ssm_menu 模組(子模組,選單模組)
也即目錄結構如下:
ssm_parent(pom)
ssm_admin
ssm_user
ssm_menu
【垂直拆分優點:】
提高了可維護性(需求變更時,修改對應的模組即可)。
提高了可拓展性(拓展業務時,增加新的模組即可,可以針對訪問量 增大、減少 某個模組的部署量)。
提高了協同開發能力(不同的團隊可以開發 不同的模組)。
【垂直拆分缺點:】
某個模組修改時(比如:頁面頻繁更換),需要對整個模組進行重新部署,增大了維護的難度。
隨著業務模組增加,各模組間必然需要進行業務互動,模組互動問題又是一個頭疼的問題。
(4)垂直應用架構總結
【垂直應用架構:】
垂直應用架構,按照業務將原來的 大單體應用 拆分成 若干個互不想幹的 小單體應用。
用於加速前端頁面開發的 web 框架(MVC)也是提高開發效率的關鍵。
【優點:】
系統間相互獨立,極大地解決了 耦合問題。
可以針對不同的業務進行 優化、可以針對不同的業務搭建叢集。
【缺點:】
使用叢集時,負載均衡相對而言比較複雜,且拓展成本高、有瓶頸。
隨著功能增加,模組隨之增多,一些通用的服務、模組也會增多,程式碼冗餘。
隨著業務增加,應用之間難免會進行 資料互動,若某個應用埠、IP 變更,需要手動進行程式碼更改(增加維護成本)。
5、架構演變 -- 分散式服務架構、SOA 架構、微服務架構
(1)分散式服務架構
隨著 垂直應用架構 模組增多,模組之間的互動不可避免,為了解決 這個問題,引出了 分散式服務架構,將 核心業務 抽取出來並獨立部署,各服務之間通過 遠端呼叫框架(PRC)進行通訊。
【分散式服務架構:】 分散式服務架構,按照業務 拆分成不同的子業務,並獨立部署在不同的伺服器上。 【垂直應用架構問題一:】 客戶對頁面要求變化大,每次修改後,都需要對應用重新部署,是比較麻煩的事情。 【解決:】 採用 前後端分離開發,將 介面 與 業務邏輯分開(水平拆分),此時只需關心介面的修改即可。 【垂直應用架構問題二:】 隨著業務模組增加,各個模組必然會進行互動,如何互動?業務部署在不同伺服器上,該如何互動? 【解決:】 採用 RPC/HTTP/HttpClient 框架進行遠端服務呼叫。 【分散式架構問題:】 新架構的改變必然帶來新的技術問題。比如: 分散式事務、分散式鎖、分散式日誌管理 等。 隨之而來的就是 分散式服務治理中介軟體: Dubbo(RPC) 以及 SpringCloud(HTTP)。
(2)SOA 架構
隨著 服務的 增多,若不對 服務進行管理,那麼容易導致 服務資源浪費(比如:使用者模組訪問量大 只部署了 10 臺伺服器,而 選單模組訪問量小,卻部署了 20 臺伺服器)、且服務間呼叫混亂(100 個服務相互呼叫若沒有條理,那將是一件非常頭疼的事情)。
為了提高 機器利用率 並 對服務進行管理,引出了 SOA 的概念。
注:
此處只是簡單的介紹了下概念,詳情請自行 谷歌、百度 瞭解(有時間再詳細研究研究)。
【SOA:】 SOA 是 Service-Oriented Architecture 的簡寫,即 面向服務架構。 指的是 根據實際業務,將系統拆分成合適的、獨立部署的服務,各服務相互獨立,通過 呼叫中心 完成 各服務之間的 呼叫 以及 管理。 【缺點:】 依賴於 中心化服務發現機制。 SOA 採用 SOAP 協議(HTTP + XML),而 XML 報文存在大量冗餘資料,影響傳輸效率。
(3)微服務
微服務是 基於 SOA 架構演變而來,去除了 SOA 架構中 ESB 訊息匯流排,採用 HTTP + JSON(RESTful )進行傳輸。其劃分粒度比 SOA 更精細。
【微服務架構:】
微服務架構 指的是 將單個應用程式 劃分為 若干個互不相干的小應用,每個小應用都是一個服務,服務之間相互協調、配合,從而為使用者提供最終價值。
每個服務執行在獨立的程序中,服務之間通常採用 輕量級的通訊機制(通常是基於 HTTP 的 RESTful API),每個服務均是基於 具體業務進行構建,並可以獨立的 部署到生產環境中。
【本質:】
微服務的目的是有效的拆分應用(將功能分散到各個服務中,降低系統耦合度),實現敏捷開發 與 部署。
微服務關鍵點在於 系統要提供一套基礎的架構,使得微服務可以獨立部署、執行、升級,且各個服務 在結構 上鬆耦合,在功能上為一個統一的整體。
注:
統一指的是:統一的安全策略、統一的許可權管理、統一的日誌處理 等。
【優點:】
微服務每個模組就等同於一個獨立的專案,可以使用不同的開發技術,使開發模式更靈活。
每個模組都有獨立的資料庫,可以選擇不同的儲存方式。比如:redis、mysql。
微服務的拆分粒度 比 SOA 更精細,複用性更強(提高開發效率)。
【缺點:】
微服務過多,服務的管理成本將隨之提高。
技術要求變高(分散式事務、分散式鎖、分散式日誌、SpringCloud、Dubbo 等一系列知識都需要學習)。
6、什麼是 SpringCloud?
(1)相關地址:
【SpringCloud 官網地址:】 https://spring.io/projects/spring-cloud 【SpringCloud 中文文件:】 https://www.bookstack.cn/read/spring-cloud-docs/docs-index.md
(2)基本認識
Spring Cloud 是分散式微服務架構下的一站式解決方案,是各個微服務架構技術實現的集合體。
注:
Spring Boot 可以快速構建單個微服務。
Spring Cloud 將多個 Spring Boot 構建的微服務整合並管理起來,並提供一系列處理(比如:服務發現、配置管理、訊息匯流排、負載均衡、斷路器、資料監控等)。
7、微服務問題 以及 技術實現
(1)背景
SpringCloud 是解決 微服務架構 而存在的,其針對 微服務架構 一系列技術問題 都做出了相關實現,當然隨著技術的進步,有些技術已經停止更新、維護了,逐步被新技術替代(學無止境)。
此處從整體上了解一下 SpringCloud 有哪些技術,後續再逐步深入。
(2)微服務問題
【微服務相關問題:】
服務註冊與發現、服務配置中心
服務呼叫、服務負載均衡
服務閘道器
服務熔斷、服務降級
服務匯流排
...
(3)技術實現
【技術實現:】
服務註冊與發現:
Eureka 停止維護了,不推薦使用。
ZooKeeper
Consul
Nacos 阿里開源的產品,推薦使用
服務配置中心:
Config
Nacos 推薦使用
服務呼叫、負載均衡:
Ribbon 停止更新了(維護狀態),不推薦使用。
Loadbalancer 作為 Ribbon 的替代產品。
Feign 停止更新了(維護狀態),不推薦使用
OpenFeign Spring 推出的 Feign 的替代產品(推薦使用)。
服務閘道器:
Zuul 停止維護了,不推薦使用。
Zuul2 還沒出來(已經涼涼了)。
Gateway Spring 推出的替代產品(推薦使用)。
服務降級:
Hystrix 停止維護了,不推薦使用。
Resilience4j 替代產品,國外使用多。
Sentienl 替代產品,國內使用多(推薦使用)。
服務匯流排:
Bus
Nacos 推薦使用。
注:
Nacos 功能還是比較強大的(可以替換多個元件),重點關注。
各個元件具體功能後續介紹,此處暫時略過。。。
(4)SpringCloud 版本選擇
進入官網,可以檢視到當前最新版本的 SpringCloud。
SpringCloud 是基於 SpringBoot 開發的,其歷史版本與 SpringBoot 對應版本如下。
當然,還是需要進入不同版本的 SpringCloud (Reference Doc.)檢視官方推薦配置。
二、程式碼拆分演示(maven 聚合)
1、構建普通的 web 專案
(1)基本說明
【專案基本說明:】 此專案僅供參考,不與資料庫進行互動。 建立 ControllerA、ServiceA 表示業務 A。 建立 ControllerB、ServiceB 表示業務 B。 使用 IDEA 利用 maven 構建 SSM 專案 可參考: https://www.cnblogs.com/l-y-h/p/12030104.html 或者 https://www.cnblogs.com/l-y-h/p/14010034.html#_label0_3
(2)使用 IDEA 利用 maven 構建一個普通的 web 工程
可參考:https://www.cnblogs.com/l-y-h/p/11454933.html
Step1:建立一個 web 工程(選擇 maven-archetype-webapp 模板,會自動生成 webapp 資料夾)
Step2:引入 依賴(Spring MVC)
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency> 【pom.xml(注意:<packaging>war</packaging> 是 war、非 pom)】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>ssm</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>ssm Maven Webapp</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </project>
Step3:配置基本 web 環境(Spring、SpringMVC)
【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_3_1.xsd" version="3.1"> <!-- step1: 配置全域性的引數,啟動Spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <!-- 若沒有提供值,預設會去找/WEB-INF/applicationContext.xml。 --> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- step2: 配置SpringMVC的前端控制器,用於攔截所有的請求 --> <servlet> <servlet-name>springmvcDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!-- 若沒有提供值,預設會去找WEB-INF/*-servlet.xml。 --> <param-value>classpath:dispatcher-servlet.xml</param-value> </init-param> <!-- 啟動優先順序,數值越小優先順序越大 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvcDispatcherServlet</servlet-name> <!-- 將DispatcherServlet請求對映配置為"/",則Spring MVC將捕獲Web容器所有的請求,包括靜態資源的請求 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- step3: characterEncodingFilter字元編碼過濾器,放在所有過濾器的前面 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <!--要使用的字符集,一般我們使用UTF-8(保險起見UTF-8最好)--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <!--是否強制設定request的編碼為encoding,預設false,不建議更改--> <param-name>forceRequestEncoding</param-name> <param-value>false</param-value> </init-param> <init-param> <!--是否強制設定response的編碼為encoding,建議設定為true--> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <!--這裡不能留空或者直接寫 ' / ' ,否則可能不起作用--> <url-pattern>/*</url-pattern> </filter-mapping> <!-- step4: 配置過濾器,將post請求轉為delete,put --> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app> 【applicationContext.xml】 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <!-- step1: 配置包掃描方式。掃描所有包,但是排除Controller層 --> <context:component-scan base-package="com.lyh.ssm"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans> 【dispatcher-servlet.xml】 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- step1: 配置Controller掃描方式 --> <!-- 使用元件掃描的方式可以一次掃描多個Controller,只需指定包路徑即可 --> <context:component-scan base-package="com.lyh.ssm" use-default-filters="false"> <!-- 一般在SpringMVC的配置裡,只掃描Controller層,Spring配置中掃描所有包,但是排除Controller層。 context:include-filter要注意,如果base-package掃描的不是最終包,那麼其他包還是會掃描、載入,如果在SpringMVC的配置中這麼做,會導致Spring不能處理事務, 所以此時需要在<context:component-scan>標籤上,增加use-default-filters="false",就是真的只掃描context:include-filter包括的內容--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- step2: 配置檢視解析器 --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/><!--設定JSP檔案的目錄位置--> <property name="suffix" value=".jsp"/> </bean> <!-- step3: 標準配置 --> <!-- 將springmvc不能處理的請求交給 spring 容器處理 --> <mvc:default-servlet-handler/> <!-- 簡化註解配置,並提供更高階的功能 --> <mvc:annotation-driven /> </beans>
Step4:編寫 基本 業務程式碼。
【ControllerA】 package com.lyh.ssm.controller; import com.lyh.ssm.service.ServiceA; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerA { @Autowired private ServiceA serviceA; @GetMapping("/testA") public String testContollerA() { return serviceA.testA(); } } 【ControllerB】 package com.lyh.ssm.controller; import com.lyh.ssm.service.ServiceB; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerB { @Autowired private ServiceB serviceB; @GetMapping("/testB") public String testControllerB() { return serviceB.testB(); } } 【ServiceA】 package com.lyh.ssm.service; import org.springframework.stereotype.Service; @Service public class ServiceA { public String testA() { return "test serviceA"; } } 【ServiceB】 package com.lyh.ssm.service; import org.springframework.stereotype.Service; @Service public class ServiceB { public String testB() { return "test serviceB"; } }
Step5:配置、啟動 tomcat。
Step6:訪問 testA()、testB()。正常訪問 也即基本 web 工程已搭建完成。
2、水平拆分 web 專案
(1)基本說明
【水平拆分說明:】
將上面簡單的 web 工程 做水平拆分,按照邏輯分層對程式碼進行拆分,
將 controller、service 層分別抽取出來,並做成 war 包或者 jar 包,需要時引入依賴即可。
【使用 Maven 聚合的方式可以演示:】
Step1:構建一個 ssm_parent 工程(父工程,聚合下面的子工程,pom)
Step2:構建一個 ssm_service 工程(子工程,存放 業務邏輯層類以及介面,jar)
Step3:構建一個 ssm_controller 工程(子工程,存放 控制層類,war)
也即目錄結構如下:
ssm_parent(pom)
ssm_service(jar)
ssm_controller(war)
(2)使用 maven 聚合的方式進行水平拆分
Step1:使用 maven 建立一個父工程(maven-archetype-site-simple)。
刪除 src 資料夾(無用資料夾),選擇 maven-archetype-site-simple 目的是使 pom.xml 中 packaging 為 <packaging>pom</packaging>。
注:
模板隨意選擇,選擇 maven-archetype-quickstart 亦可,保證 <packaging>pom</packaging>。
Step2:在 ssm_parent 上 右鍵 選擇 建立 Module。
並使用 maven 模板(maven-archetype-quickstart)建立一個模組(ssm_service)。
Step3:將 普通 web 工程中 service 程式碼 抽取出來,並放入 ssm_service 中。
【ServiceA】 package com.lyh.ssm.service; import org.springframework.stereotype.Service; @Service public class ServiceA { public String testA() { return "test serviceA"; } } 【ServiceB】 package com.lyh.ssm.service; import org.springframework.stereotype.Service; @Service public class ServiceB { public String testB() { return "test serviceB"; } }
Step4:在 父工程(ssm_parent)或者 當前工程(ssm_service)中引入 SpringMVC 依賴包。
【SpringMVC 依賴:】
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
Step5:在 ssm_parent 上 右鍵 選擇 建立 Module。
並使用 maven 模板(maven-archetype-webapp)建立一個模組(ssm_controller)。
Step6:將普通 web 工程中 相關程式碼(controller、配置檔案)抽取出來,放入 ssm_controller 中。
【ControllerA】 package com.lyh.ssm.controller; import com.lyh.ssm.service.ServiceA; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerA { @Autowired private ServiceA serviceA; @GetMapping("/testA") public String testContollerA() { return serviceA.testA(); } } 【ControllerB】 package com.lyh.ssm.controller; import com.lyh.ssm.service.ServiceB; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerB { @Autowired private ServiceB serviceB; @GetMapping("/testB") public String testControllerB() { return serviceB.testB(); } } 【applicationContext.xml】 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <!-- step1: 配置包掃描方式。掃描所有包,但是排除Controller層 --> <context:component-scan base-package="com.lyh.ssm"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans> 【dispatcher-servlet.xml】 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- step1: 配置Controller掃描方式 --> <!-- 使用元件掃描的方式可以一次掃描多個Controller,只需指定包路徑即可 --> <context:component-scan base-package="com.lyh.ssm" use-default-filters="false"> <!-- 一般在SpringMVC的配置裡,只掃描Controller層,Spring配置中掃描所有包,但是排除Controller層。 context:include-filter要注意,如果base-package掃描的不是最終包,那麼其他包還是會掃描、載入,如果在SpringMVC的配置中這麼做,會導致Spring不能處理事務, 所以此時需要在<context:component-scan>標籤上,增加use-default-filters="false",就是真的只掃描context:include-filter包括的內容--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- step2: 配置檢視解析器 --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/><!--設定JSP檔案的目錄位置--> <property name="suffix" value=".jsp"/> </bean> <!-- step3: 標準配置 --> <!-- 將springmvc不能處理的請求交給 spring 容器處理 --> <mvc:default-servlet-handler/> <!-- 簡化註解配置,並提供更高階的功能 --> <mvc:annotation-driven /> </beans> 【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_3_1.xsd" version="3.1"> <!-- step1: 配置全域性的引數,啟動Spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <!-- 若沒有提供值,預設會去找/WEB-INF/applicationContext.xml。 --> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- step2: 配置SpringMVC的前端控制器,用於攔截所有的請求 --> <servlet> <servlet-name>springmvcDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <!-- 若沒有提供值,預設會去找WEB-INF/*-servlet.xml。 --> <param-value>classpath:dispatcher-servlet.xml</param-value> </init-param> <!-- 啟動優先順序,數值越小優先順序越大 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvcDispatcherServlet</servlet-name> <!-- 將DispatcherServlet請求對映配置為"/",則Spring MVC將捕獲Web容器所有的請求,包括靜態資源的請求 --> <url-pattern>/</url-pattern> </servlet-mapping> <!-- step3: characterEncodingFilter字元編碼過濾器,放在所有過濾器的前面 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <!--要使用的字符集,一般我們使用UTF-8(保險起見UTF-8最好)--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <!--是否強制設定request的編碼為encoding,預設false,不建議更改--> <param-name>forceRequestEncoding</param-name> <param-value>false</param-value> </init-param> <init-param> <!--是否強制設定response的編碼為encoding,建議設定為true--> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <!--這裡不能留空或者直接寫 ' / ' ,否則可能不起作用--> <url-pattern>/*</url-pattern> </filter-mapping> <!-- step4: 配置過濾器,將post請求轉為delete,put --> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Step7:在 ssm_controller 中引入 ssm_service.jar 包。
<dependency> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_service</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
Step8:使用 tomcat 啟動 ssm_controller 工程,即可訪問專案。
Step9:使用 tomcat7 外掛來啟動 maven 聚合工程。
【在父工程 pom.xml 中引入 tomcat7 外掛】
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
</plugin>
</plugins>
</build>
3、垂直拆分 web 專案
(1)基本說明
【垂直拆分說明:】 將上面簡單的 web 工程 做垂直拆分,按照業務對程式碼進行拆分, 將 業務A、業務 B 分別抽取出來,並做成獨立的 war 包。 注: 若拆分後仍使用配置檔案的方式進行專案構建,那麼程式碼冗餘將非常多。 所以一般均使用 SpringBoot 簡化開發(約定 > 配置)。 【使用 Maven 聚合的方式可以演示:】 Step1:構建一個 ssm_parent 工程(父工程,聚合下面的子工程,pom) Step2:構建一個 ssm_serviceA 工程(子工程,存放 業務A,war) Step3:構建一個 ssm_serviceB 工程(子工程,存放 業務B,war) 也即目錄結構如下: ssm_parent(pom) ssm_serviceA(war) ssm_serviceB(war)
(2)使用 maven 聚合的方式進行垂直拆分
此處 以 SpringBoot 作為專案構建的基礎。
Step1:使用 maven 構建一個父工程 ssm_parent。
Step2:ssm_parent 專案上右鍵選擇 New -> Module 並選擇 Spirng Initializr。
使用 SpringBoot 建立 serviceA 專案。
Step3:從普通 web 工程中 抽取出 業務A 的程式碼放入 ssm_serviceA 中。
【ControllerA】 package com.lyh.ssm.aservice.controller; import com.lyh.ssm.aservice.service.ServiceA; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerA { @Autowired private ServiceA serviceA; @GetMapping("/testA") public String testContollerA() { return serviceA.testA(); } } 【ServiceA】 package com.lyh.ssm.aservice.service; import org.springframework.stereotype.Service; @Service public class ServiceA { public String testA() { return "test serviceA"; } }
Step4:修改 父工程(ssm_parent) 以及 當前工程(ssm_serviceA)pom.xml 並引入依賴。
修改 ssm_serviceA 的 <parent>,使其指向父工程(ssm_parent)。
在父工程中通過 <module> 管理 子工程。
在父工程中 通過 <dependencyManagement> 宣告依賴、管理版本。
在子工程中 通過 <dependency> 引入需要的依賴。
【ssm_parent 的 pom.xml 為:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>ssm_serviceA</module> </modules> <properties> <java.version>1.8</java.version> <web.version>2.4.0</web.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${web.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${web.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project> 【ssm_serviceA 的 pom.xml 為:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>aservice</artifactId> <name>ssm_serviceA</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
Step5:修改 ssm_serviceA 的配置檔案(埠號、服務名等)
修改 application.properties 或者 application.yml 檔案。
【application.yml】 server: port: 9000 spring: application: name: ssm_serviceA
Step6:同理,建立 SpingBoot 專案 ssm_serviceB,並從 普通 web 工程中 抽取 業務B 程式碼放入其中。同樣修改 pom.xml(指向父工程,引入 web 依賴) 以及 配置檔案。
【ControllerB】 package com.lyh.ssm.bservice.controller; import com.lyh.ssm.bservice.service.ServiceB; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class ControllerB { @Autowired private ServiceB serviceB; @GetMapping("/testB") public String testContollerB() { return serviceB.testB(); } } 【ServiceB】 package com.lyh.ssm.bservice.service; import org.springframework.stereotype.Service; @Service public class ServiceB { public String testB() { return "test serviceB"; } } 【ssm_serviceB 的 pom.xml 為:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>bservice</artifactId> <name>bservice</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project> 【application.yml】 server: port: 9010 spring: application: name: ssm_serviceB 【ssm_parent 的 pom.xml 為:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>ssm_serviceA</module> <module>ssm_serviceB</module> </modules> <properties> <java.version>1.8</java.version> <web.version>2.4.0</web.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${web.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${web.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>
Step7:通過兩個專案的啟動類 可以 分別啟動兩個專案。
或者 直接 mvn install 父工程(ssm_parent),然後執行打包好的 jar 包。
4、使用 maven 聚合的注意事項
(1)父工程的 pom.xml 檔案
【聚合專案 父工程:】 使用 maven 建立聚合工程時,父工程中 使用 <packaging>pom</packaging> 指定型別為 pom。 父工程一般 用於進行 依賴宣告 以及 版本控制。 注: 通過 <properties> 標籤 以及 ${} 可以進行依賴(jar)版本的管理。 使用 <dependencyManagement> 標籤可以進行依賴宣告。 使用 <modules> 標籤管理 子模組。 【父工程 pom.xml 舉例:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>ssm_serviceA</module> <module>ssm_serviceB</module> </modules> <properties> <java.version>1.8</java.version> <web.version>2.4.0</web.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${web.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${web.version}</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>
(2)子工程的 pom.xml 檔案
【聚合專案 子工程:】 使用 maven 建立子工程時,使用 <parent> 標籤指定 父工程。 使用 <dependencies> 標籤按需引入依賴,若父工程使用 <dependencyManagement> 進行版本控制,則子工程引入依賴時可以不指定版本 <version>(便於統一管理)。 【子工程 pom.xml 舉例:】 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.lyh.ssm</groupId> <artifactId>ssm_parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>bservice</artifactId> <name>bservice</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
(3)<dependencyManagement> 與 <dependencies> 區別
<dependencyManagement> 一般出現在 父工程中,用於 宣告依賴 以及 版本控制,但是並沒有真正引入依賴。
<dependencies> 是真正的引入依賴。
父工程中 使用 <dependencyManagement> 進行了版本控制,若子工程中 <dependencies> 引入依賴時使用 <version> 指定了版本,則以子工程的版本為主。若子工程中沒有使用 <version>,則以父工程定義的 version 為主。