主頁 > 後端開發 > 學習一下 SpringCloud (一)-- 從單體架構到微服務架構、代碼拆分(maven 聚合)

學習一下 SpringCloud (一)-- 從單體架構到微服務架構、代碼拆分(maven 聚合)

2020-12-09 06:55:38 後端開發

一、架構演變

1、系統架構、集群、分布式系統 簡單理解

(1)什么是系統架構?

【什么是系統架構?】
    系統架構 描述了 在應用程式內部,如何根據 業務、技術、靈活性、可擴展性、可維護性 等因素,將系統劃分成不同的部分并使這些部分相互分工、協作,從而提高系統的性能,
    
【簡單的理解:】
    系統架構是 程式運行 的基石、其決定了程式是否能正確、有效的構建 以及 穩定的運行,

 

(2)集群

【什么是集群?】
    計算機集群簡稱集群,是一種計算機系統,它通過一組松散集成的計算機軟體或硬體連接起來、高度緊密地協作完成計算作業,
    在某種意義上,他們可以被看作是一臺計算機,
    集群系統中的單個計算機通常稱為節點,通常通過局域網連接(或者其它的連接方式),
    集群計算機通常用來改進單個計算機的計算速度或可靠性,

【簡單的理解:】
    通過多臺計算機完成同一個作業,達到更高的效率,
    兩機或多機內容、作業程序等完全一樣,如果一臺死機,另一臺可以起作用,
    同一個業務,部署在多個服務器上(不同的服務器運行同樣的代碼,干同一件事),

 

(3)分布式系統

【什么是分布式系統?】
    分布式系統是一組計算機,通過網路相互連接,傳遞訊息與通信后,協調它們的行為而形成的系統,
    組件之間彼此進行互動從而實作一個共同的目標,

【簡單的理解:】
    通過多臺計算機相互作用完成一個作業,每個計算機負責一個功能模塊,將多個計算機組合起來從而完成一個大任務,
    一個業務拆分為多個子業務,部署在不同的服務器上(不同的服務器,運行不同的代碼,為了同一個目的),
注:
    分布式與集群不沖突,可以存在分布式集群,即將一個專案拆分為不同的功能模塊后,對各個不同的功能模塊實作集群,

 

(4)架構演變
  Dubbo 官網將系統架構分為 單體架構、垂直架構、分布式服務架構、流計算架構,
  可參考:http://dubbo.apache.org/docs/v2.7/user/preface/background/
注:
  下面的 系統演變程序 以此為 基礎 進行展開,有不對的地方還望不吝賜教,

 

 

 

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" >
        <property name="prefix" value="https://www.cnblogs.com/WEB-INF/"/><!--設定JSP檔案的目錄位置-->
        <property name="suffix" value="https://www.cnblogs.com/l-y-h/p/.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="https://www.cnblogs.com/WEB-INF/"/><!--設定JSP檔案的目錄位置-->
        <property name="suffix" value="https://www.cnblogs.com/l-y-h/p/.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 為主,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/231739.html

標籤:Java

上一篇:H5微信網頁授權java后端SpringBoot實作

下一篇:Redis面試題匯總(附答案),面試突擊專用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more