主頁 > 軟體設計 > MyBatis 版本升級引發的線上問題

MyBatis 版本升級引發的線上問題

2020-11-04 14:40:35 軟體設計

目錄

報警原因定位

MyBatis升級 3.2.4版本的官方 Release公告

以版本3.2.3為例,MyBatis構建 SQL陳述句程序的原理分析

以版本3.2.4為例,相比版本3.2.3,MyBatis構建SQL陳述句程序的變化分析

總結


MyBatis上線前后的版本:上線前(3.2.3)上線后(3.4.6)

服務上線后,開始陸續出現了一些更新系統互動日志方面的報警,這屬于系統的輔助流程,報警如下代碼所示,我們發現都是跟 MyBatis相關的報警,說明在進行型別轉換 [ibatis.type.TypeException]的時候,系統產生了強轉錯誤,

更新開票請求回傳日志, id:{#######}, response:{{"code":XXX,"data":{"callType":3,"code":XXX,"msg":"XXXX","shopId":XXXXX,"taxPlateDockType":"XXXXXXX"},"msg":"XXXXX","success":XXXX}}
nested execption is org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property='updateTime', mode=IN, javaType=class java.lang.String,
jdbcTyp=null,resultMapId='null',jdbcTypeName='null',expression='null'}.Cause org.apache.ibatis.type.TypeException,Error setting non null parameter #2 with JdbcType null. Try setting a
different Jdbc Type for this parameter or a different configuration property.Cause java.lang.ClassCastException:java.time.LocalDateTime cannot be cast to java.lang.String

報警這一塊代碼,屬于歷史功能,如果失敗并不會影響主流程,但在定位期間,如果頻繁報警的話,就會造成一定的干擾,因此,我們馬上采取了回滾操作,直至報警消失,然后再進行問題的定位和分析,

報警原因定位


在回滾完畢后,我們開始具體分析報警產生的主要原因,于是進行了以下幾步的排查,

第一步,查看了報警的 Mapper方法,如下所示:接收引數,根據主鍵ID更新具體回應內容和時間,入參有3個,型別分別為longStringLocalDateTime

int updateResponse(@Param("id")long id, @Param("response")String response, @Param("updateTime")LocalDateTime updateTime);

第二步,我們查看了 Mapper方法對應的 XML檔案,如下代碼段所示,對應的 parameterType型別是String,而實際引數的型別包括 longStringLocalDateTime

<update id="updateResponse" parameterType="java.lang.String">
    UPDATE business_log
    SET response = #{response}, update_time = #{updateTime}
    WHERE id = #{id}
</update>

第三步,報警的內容是:MyBatis在處理 SQL陳述句時,發現不能將 LocalDateTime轉型為String,這一段邏輯在上線前是可以正常運行的,并且上線的業務邏輯對這段歷史代碼無改動,因此,我們猜測是因為 MyBatis的版本發生了變化導致的,對某些歷史功能不再支持了,MyBatis上線前后的版本:上線前(3.2.3)上線后(3.4.6)

第四步,我們通過第三步可以得到,MyBatis的版本直接升了兩個大版本,因此我們可以基本將原因猜測為 MyBatis升級跨度較大,導致部分歷史功能沒有兼容支持,從而引起線上 SQL的更新報錯,

第五步,為了具體驗證第四步的想法,我們通過 UT的方式,將 MyBatis的版本不斷從 3.4.6往下降,直至沒有報錯的位置,最終的定位是:當 MyBatis版本為3.2.3時,線上代碼是正常可用的,但只要升一個版本,也就是自 3.2.4開始,就開始不兼容目前的用法,不過,我們當時的思路并不是很好,應該從小版本逐個往上升或者使用二分法,可以加速定位版本的效率,

最后,我們定位到了產生報警的根本問題,MyBatis自 3.2.4開始就不支持目前系統內的 SQL Mapper的用法,因此在升級后,線上就出現了頻繁報警的問題,問題已經定位,但是還有很多事情我們需要弄清楚,為什么版本升級后就不兼容歷史的用法?具體是哪一塊內容不兼容?背后的原理又是什么?下文,我們會詳細進行分析,

MyBatis升級 3.2.4版本的官方 Release公告


首先,從報錯的原因上來看,請注意這句話:“Caused by: java.lang.ClassCastException: java.lang.LocalDateTime cannot be cast to java.lang.String.”MyBatis在構建 SQL陳述句時,發現時間欄位型別 LocalDateTime不能強制轉為 String型別,而這個 SQL對應的 XML配置在 3.2.3的版本是可以正常使用的,那么我們先從 MyBatis的 Release Log上查看 3.2.4版本到底發生了什么變化,

An special remark about this feature. Previous versions ignored the “parameterType” attribute and used the actual parameter to calculate bindings. This version builds the binding information during startup and the “parameterType” attribute is used if present (though it is still optional), so in case you had a wrong value for it you will have to change it.

從官網的 Release Log可以看到,MyBatis在3.2.4以前的版本,會忽略 XML中的 parameterType這個屬性,并且使用真實的變數型別進行值的處理,但在 3.2.4及以后的版本中,這個屬性就被啟用了,如果出現型別不匹配的話,就會出現轉型失敗的報錯,這也提示我們開發者,在升級版本時,需要檢查系統內的 XML配置,使型別進行匹配,或者不設定該屬性,讓 MyBatis自行進行計算

根據以上內容,我們可以了解到,在版本升級后,MyBatis在構建 SQL陳述句,在獲取欄位值時的邏輯發生了變化,接下來我們將通過一個簡單的示例,來了解一下 MyBatis在獲取欄位值這一塊的具體代碼流程是怎樣的,以 3.2.3版本為例,

以版本3.2.3為例,MyBatis構建 SQL陳述句程序的原理分析


我們看一下配置,首先定義一個通過主鍵id獲取學生資訊的方法,仿造系統內的歷史代碼,我們將 parameterType定義為 java.lang.String,這和方法對應的引數 int并不相同,

<!--public StudentEntity getStudentById(@Param("id") int id);-->
<select id="getStudentById" parameterType="java.lang.String"     resultType="entity.StudentEntity">
    SELECT id,name,age FROM student WHERE id = #{id}
</select>

MyBatis 框架要做的事情,就是在運行 getStudentById(2)的時候,將 #{id}進行替換,使 SQL陳述句變成 SELECT id,name,age FROM student WHERE id = 2,MyBatis要將 SQL陳述句完整替換成帶引數值的版本,需要經歷框架初始化以及實際運行時動態替換這兩個部分,因為 MyBatis的代碼非常多,接下來我們主要闡釋和本次案例相關的內容,

在框架初始化階段,主要包括以下流程,如下圖所示:

框架初始化階段,有一些組件會被構建,逐一做個簡單的介紹:
【1】SqlSession:作為 MyBatis作業的主要頂層API,表示和資料庫互動的會話,完成必要的資料庫增刪改查功能,
【2】資料庫增刪改查功能:負責根據用戶傳遞的 parameterObject,動態地生成 SQL陳述句,將資訊封裝到 BoundSql物件中,并回傳,
【3】Configuration:MyBatis所有的配置資訊都維持在 Configuration物件之中,

接下來,我們主要關注 SqlSource,這個類會負責生成SQL陳述句,這也是本次案例中,3.2.3和3.2.4差異比較大的一個地方,下面,我們會介紹一些原始碼,

在構建 Configuration的程序中,會涉及到構建對應每一條 SQL陳述句對應的 MappedStatementparameterTypeClass就是根據我們在 XML配置中寫的 parameterType轉換而來,值為 java.lang.String,在構建 SqlSource時,傳入這個引數,如下圖所示:

在 SqlSource的構建中,parameterType引數其實是被忽略不用的,并沒有繼續往下傳遞,這跟官方的描述是一致的,因為 3.2.4之前這個 parameterType屬性被忽略了,然后就創建了 DynamicSqlSource,這個類主要是用于處理 MyBatis動態 SQL的類,如下圖所示:

在框架初始化的階段,需要介紹的內容,在 3.2.3版本已經介紹完畢,當執行 getStudentById方法時,MyBatis的流程如下圖所示,因受限于圖片長度,我們對布局進行了一些調整:

在具體執行階段,也涉及到一些組件,我們需要做簡單的了解:
【1】SqlSession:作為 MyBatis作業的主要頂層API,表示和資料庫互動的會話,完成必要資料庫增刪改查功能,
【2】Executor:MyBatis執行器,這是 MyBatis調度的核心,負責 SQL陳述句的生成和查詢快取的維護,
【3】BoundSql:表示動態生成的 SQL陳述句以及相應的引數資訊,
【4】StatementHandler:封裝了JDBC Statement操作,負責對JDBC statement的操作,如設定引數、將 Statement結果集轉換成 List集合等等,
【5】ParameterHandler:負責對用戶傳遞的引數轉換成 JDBC Statement 所需要的引數,
【6】TypeHandler:負責 Java資料型別和 JDBC資料型別之間的映射和轉換,

我們主要關注獲取 BoundSql以及引數化陳述句的流程,這也是3.2.3和3.2.4差異比較大的一個地方,在進入 Executor的 Query方法后,會首先通過對應的 MappedStatement來獲取 BoundSql,用來幫助我們動態生成SQL陳述句,里面系結了對應的 SQL以及引數映射關系,在構建框架階段,我們使用的 SqlSource是 DynamicSqlSource,通過這個類來生成獲取 BoundSql,如下圖所示:

通過上圖的代碼,我們可以得知,parameterType在初始化階段未被使用,而是在 SQL執行時獲取到的,但獲取到的型別是 parameterObject對應的型別,這個類是用來記錄 Mapper方法上對應的引數,如下圖所示,它并非在 SQL組態檔中標注的java.lang.String,

然后我們通過 SqlSourceBuilder的 parse方法對 SQL以及獲取到的型別進行再次處理,其中的流程代碼比較長,在這個程序中,我們主要去構建 SQL的引數和 Java型別的系結關系,MyBatis依賴這個系結關系,使用對應的 TypeHandler去進行值的轉換,

呼叫鏈路是SqlSourceParser.parse -> 內部類 ParameterMappingTokenHandler.handleToken -> 私有方法 buildParameterMapping,如下圖中的代碼所示,因為當前的 parameterType為 MapperMethod$ParamMap,經過了多個if判斷,判定當前 property id的propertyType為 Object.class型別,接下來,構建 SQL的引數和 Java型別的系結關系 ParameterMapping,再進行回傳,

構建完成的 ParameterMapping的結構如下圖中的代碼所示,引數id對應的 javaType型別為 java.lang.Object,對應的 TypeHander處理器為 UnknownTypeHandler,也就是未找到合適的 TypeHandler的兜底選項,

接下來,流程就會流轉到Executor,在org.apache.ibatis.executor.SimpleExecutor#doQuery進行查詢時,會根據當前的SQL型別,生成對應的StatementHandler,因為我們目前都是用的預編譯SQL,因此生成的statementHandler就是 PreparedStatementHandler,熟悉 JDBC的小伙伴應該馬上可以猜到對應的陳述句是什么型別了,然后,我們對這句 SQL陳述句進行填充,如下圖中的代碼所示,我們會通過PreparedStatementHandler的 parameterize方法對 Statement進行引數化,也就是進行填充,

圖10 PrepareStatement處理程序

在 PreparedStatementHandler進行引數化時,會將引數化的職責交給 DefaultParameterHandler處理,如下圖中的代碼所示,我們主要關注紅線部分,首先會獲取 ParameterMapping對應的 TypeHander,如前文所述,獲取到的是UnknownTypeHandler,然后會通過setParameter方法,將引數id替換成對應的值,

在Typehandler的流程里,首先會進入BaseTypeHandler,然后在具體設定時,會進入子類的方法,在UnknownTypeHandler,首先會再次對引數 parameter進行決議,判斷最正確的 TypeHandler型別,如下圖中的代碼所示:

圖12 獲取可用TypeHandler

在 resolveTypeHandler方法中,因為已知了引數值的型別,通過 Integer這個 class在 typeHandlerRegistry中尋找對應的TypeHandler,TypeHandlerRegistry是 MyBatis啟動時內置好的,代表 Java物件型別和 TypeHandler的映射關系,有興趣的同學可以進入這個類詳細看下,在這個例子中,我們會直接獲取到 IntegerHandler,如下圖中的代碼所示:

圖13 獲取IntegerHandler

在獲取到 IntegerHandler后,我們就可以使用 IntegerTypeHandler的setInt方法,對SQL陳述句中的引數進行替換,如圖中的代碼所示,SQL陳述句被成功替換:

圖14 IntegerHander值替換

后續就是執行 SQL并處理回傳結果,這就不在本文的討論范圍內了,從上文的分析中,我們可以了解到,在3.2.3及以下版本,MyBatis會忽略 parameterType,在真正進行SQL轉換時,重新根據SQL方法入參型別,然后計算合適的 TypeHandler處理器,所以本案例中的代碼在3.2.3版本時,它在運行時是正常的,

以版本3.2.4為例,相比版本3.2.3,MyBatis構建SQL陳述句程序的變化分析


在前一章節中,我們得知 MyBatis在運行 SQL階段重新計算引數對應的 TypeHandler,然后進行SQL引數的替換,那么,在版本3.2.4中,MyBatis做了什么改動,從而導致了原有的使用方式變得不可用呢?從官方的Release Log來看,版本3.2.4做了這樣的一個改動,

This version builds the binding information during startup and the “parameterType” attribute is used

這個意思是說:parameterType會在框架初始化階段階段就被使用到,我們將分析的重點放在構建階段,因為負責處理系結關系的 BoundSql由配置階段的 SqlSource生成,我們主要查看 SqlSource的構建,在3.2.4中發生了什么變化,如下圖所示,與3.2.3不同,3.2.4首先判斷了是否為動態SQL,在非動態SQL情況下,才會將 parameterType java.lang.String作為引數,傳入SqlSource的構造方法,

圖15 生成SqlSource

而后續流程與3.2.3一致,因為parameter型別為 java.lang.String,在構建 parameterMapping時,使用的型別就是 java.lang.String,

圖16 構建ParameterMapping與3.2.3版本的差異

構建 ParameterMapping與3.2.3版本的差異

因為在框架初始化階段,SqlSource的 ParameterMapping中id對應的型別就是 java.lang.String,這就導致在進行 SQL陳述句的替換時,獲取到的 TypeHandler是 StringTypeHandler,如下圖所示:

圖17  整數型別的引數獲取到了StringTypeHandler

整數型別的引數獲取到了StringTypeHandler

后面的報錯原因就比較好理解了,在呼叫StringTypeHandler的 setString方法時,報出了java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 的錯誤,

總結


MyBatis 3.2.3版本支持 parameterType和實際引數型別不匹配,在執行 SQL階段,動態計算值處理器型別,在大版本升級2個版本號后,parameterType實際的型別開始生效,使用對應這個型別的 TypeHandler對SQL進行引數替換,會導致 Mapper方法中的引數和 XML中的 parameterType不匹配時,進而會出現型別轉換報錯,

這一段排查的經歷,對自己后續撰寫代碼及在系統上線時也有一些啟發,主要包括以下幾個方面:
【1】在專案升級時,需要線下進行全面回歸,要避免框架存在不兼容的用法,不然的話,就容易導致線上錯誤,
【2】開發同學可以檢查自己系統內的 MyBatis版本,如果是3.2.4以下,需要全面檢查下現在的 Mapper檔案里對于 parameterType的使用和 Mapper方法中實際的引數型別是否一致,避免升級到3.2.4及以上版本時發生轉型報錯,如果有不匹配的情況存在,需要進行修正或者不使用 parameterType,讓 MyBatis在運行 SQL時自動計算對應的型別,
【3】可以考慮使用 MyBatis-Generator來自動生成 XML和 Mapper檔案,畢竟是專業團隊在維護,穩定性相對來說會更好一些,同時能夠避免手動修改 XML檔案帶來的誤操作,
【4】可以主動關注強依賴的一些開源框架的 Release Log,不要錯過了重要的資訊,

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

標籤:其他

上一篇:模擬地下城與勇士(DNF)的裝備強化

下一篇:13張圖解分布式系統服務注冊與發現機制,給你整明白

標籤雲
其他(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)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more