文章目錄
- 規範
- 對比傳統MVC
- 小栗子
- V1.0 傳統寫法
- V2.0 引入Manager層
- 源碼
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iN2YDMzkzYjJjMxgTMmBjMyYzX1ATO0ADM0AzLcNDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
規範
對比傳統MVC
說幾個弊端
- Service層代碼臃腫
- Service層易出現大事務,事務嵌套,易出問題且難排查
- dao層混雜業務邏輯
- dao層sql語句複雜
- …
為了解決這個問題,《阿裡巴巴泰山版java開發手冊》推薦在Service層之下獨立出一個通用業務處理層(Manager層)
相比較傳統的MVC,主要增加了 Manager 層, 它有如下特征:
- 1) 對第三方平台封裝的層,預處理傳回結果及轉化異常資訊
- 2) 對 Service 層通用能力的下沉,如緩存方案、中間件通用處理
- 3) 與 DAO 層互動,對多個 DAO 的組合複用
實際開發中,
- 對于複雜業務,service調用manager層,然後把事務下沉到Manager層,Manager層不允許互相調用,不會出現事務嵌套。
- 專注于不帶業務SQL,也可以在manager層進行通用業務的dao層封裝。
- 避免複雜的join查詢,可以在manager層嚴格控制好SQL,應對複雜的SQL查詢。
簡言之, Manager 層提供原子服務接口,Service 層負責依據業務邏輯來調用原子接口。
小栗子
舉個例子說明一下Manager層的使用場景
需求:
- APP 登入的使用者,如果系統中沒有使用者,需要自動建立使用者,然後再傳回相關的使用者資訊用于展示
- 網頁端登陸的使用者,如果系統中沒有使用者,不自動建立使用者,需要使用者注冊。
反手就是一頓突突啊
V1.0 傳統寫法
package com.artisan.service;
import com.artisan.dao.UserDao;
import com.artisan.response.ResponseData;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.concurrent.TimeUnit;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
@Service
public class ServiceWithoutManager {
private final boolean APP = true;
@Autowired
private UserDao userDao;
@Transactional(rollbackFor = Throwable.class)
public ResponseData<String> buiz(Long idCard, Long name) {
// 驗證 1 假設 DB操作 校驗
String var1 = doDBCheck1();
// 驗證 2 假設 DB操作 校驗
String var2 = doDBCheck2();
// 業務 APP -- DB校驗 --- 自動建立使用者 -- 傳回使用者資訊
// 網頁 -- DB校驗 --- -- 傳回使用者資訊
doBiz(var1,var2);
return ResponseData.success("success");
}
@SneakyThrows
private void doBiz(String a ,String b) {
if(APP) {
// 模拟業務耗時
TimeUnit.MILLISECONDS.sleep(1200);
}else {
}
}
@SneakyThrows
private String doDBCheck2() {
// 模拟業務耗時
TimeUnit.MILLISECONDS.sleep(500);
return "";
}
@SneakyThrows
private String doDBCheck1() {
// 模拟業務耗時
TimeUnit.MILLISECONDS.sleep(1000);
return "";
}
}
這有啥子問題? 正常操作啊…
每日一博 - 常見的Spring事務失效&事務不復原案例集錦
讓我們來分析分析
- 典型的長事務問題 , 由于方法上有@Transactional 注解,是以驗證和業務都是使用的同一個 connection
- 對于複雜業務、複雜的驗證邏輯,會導緻整個驗證過程始終占用該 connection 連接配接,占用時間可能會很長,直至方法結束,connection 才會交還給資料庫連接配接池。
對于複雜業務的不可預計的情況,長時間占用同一個 connection 連接配接應該盡量避免,應該盡量縮短占用時間。
@Transactional 注解, AOP 實作,本質就是在目标方法執行前後進行攔截。 在目标方法執行前加入或建立一個事務,在執行方法執行後,根據實際情況選擇送出或是復原事務。
當 Spring 遇到該注解時,會自動從資料庫連接配接池中擷取 connection,并開啟事務然後綁定到 ThreadLocal 上,對于@Transactional注解包裹的整個方法都是使用同一個connection連接配接。
如果出現了耗時的操作,比如三方接口調用,業務邏輯複雜,大資料處理等就會導緻占用這個connection的時間過長,資料庫連接配接一直被占用不釋放。一旦類似操作過多,進而導緻資料庫連接配接池耗盡。
V2.0 引入Manager層
将資料在 service 層準備好,然後傳遞給 manager 層,由 manager 層添加@Transactional事務注解進行資料庫操作, 盡量減少大事務帶來的危害。
源碼
https://github.com/yangshangwei/boot2