編譯期動態(tài)替換三方包中的Class文件過程詳解
背景
最近做業(yè)務(wù)時遇到一個問題,客戶想在底層數(shù)據(jù)添加一個字段,只能乖乖的添加表字段、實(shí)體添加對應(yīng)屬性,一切都在預(yù)期中進(jìn)行這,但是這個工程是經(jīng)過二開的,展示層實(shí)體沒法直接添加,于是想當(dāng)然繼承實(shí)體擴(kuò)展字段,沒想到頂層一堆Request、Response,如果一個一個進(jìn)行擴(kuò)展馬也得累死,于是就思考有沒有簡便的方法僅對目標(biāo)實(shí)體進(jìn)行操作來完成字段添加的方法。
思考過程
在Java中要在類中添加字段屬性,除了顯示編碼外,還有一種技術(shù)就是編譯期間動態(tài)修改,比如Lombok、Mapstruct等都是在編譯期動態(tài)生成代碼,提高編碼效率,所以我也考慮通過這種方式編譯期添加目標(biāo)字段屬性,百度了一通沒有合適的方式動態(tài)添加,但是手寫通過字節(jié)碼注入一定是可以實(shí)現(xiàn)的,想想成本還是有點(diǎn)高,趕緊轉(zhuǎn)換思路,既然不能動態(tài)插入字段,那能不能直接替換目標(biāo)類呢?一想到這就有戲,在Java中加載類是通過類加載器進(jìn)行加載的,有了依據(jù)后趕緊接著百度,果不然讓我發(fā)現(xiàn)一種方式,通過maven插件的方式實(shí)現(xiàn),客官接著往下看。
一般情況下不建議用這么hack的方式哈,盡量保持三方包的新鮮度,避免未來升級導(dǎo)致的兼容性問題。
主角出場
主角:maven-dependency-plugin
這僅僅是處理的一種方式,大家如果有更好的處理方式,可以放到評論區(qū),我們一起討論,互相學(xué)習(xí)進(jìn)步。
實(shí)現(xiàn)原理
通過配置maven-dependency-plugin, 可以將我們指定的dependency解壓到項目的class目錄中,設(shè)置不覆蓋本地項目相同class文件(類的全限定名相同),就實(shí)現(xiàn)了本地文件替換三方j(luò)ar中類文件的目的了。
在Java應(yīng)用中,如果存在多個同名類,最終只會加載一個目標(biāo)類,到底會加載哪個同名類是又類加載器的雙親委派機(jī)制決定的,先請求父類加載器加載,父類無法加載回到應(yīng)用程序加載器,應(yīng)用程序無法加載就會到類路徑下即class目錄中加載,如果仍然不存在會到依賴中進(jìn)行加載。
基于以上原理,實(shí)現(xiàn)類文件覆蓋就有了依據(jù),那么接下來具體實(shí)踐演示下。
使用
這里我使用commons-lang3舉例。
第一步:配置Maven插件
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.1.2</version> <executions> <execution> <!-- unpack任務(wù)標(biāo)識符,unpack是將依賴從倉庫中解壓到指定目錄 --> <id>unpack</id> <!-- unpack任務(wù)默認(rèn)執(zhí)行階段 --> <phase>generate-sources</phase> <goals> <!-- 目標(biāo)功能:unpack --> <goal>unpack</goal> </goals> <configuration> <artifactItems> <artifactItem> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <!-- 設(shè)置為false,依賴解壓到目錄時不會進(jìn)行覆蓋,設(shè)置為true則會覆蓋 --> <overWrite>false</overWrite> <!-- 目標(biāo)class文件輸出目錄 --> <outputDirectory>${project.build.directory}/classes</outputDirectory> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin>
第二步:編譯工程
這一步還沒有定義目標(biāo)類,暫時僅是將目標(biāo)三方包的類放到了class目錄下,標(biāo)紅的位置是即將要進(jìn)行修改的位置。
第三步:添加目標(biāo)類
- 包名和目標(biāo)類所在包名完全一致;
- 類名保持一致;
第四步:重新編譯工程
至此已經(jīng)能夠看到效果了,整個方式過程也比較簡單,去掉中間的編譯過程,總共兩步。
總結(jié)
這種Hack的方式在業(yè)務(wù)編碼中建議少用,通過上邊的方式雖然能解決問題,但同時也引入了一些副作用,一方面相當(dāng)于依賴包引入兩份,另一方面當(dāng)依賴包升級時可能存在疏漏??纯磳W(xué)習(xí)學(xué)習(xí),多一種解決方式多一條路,希望大家每天編碼順順利,我就先溜了!
以上就是編譯期動態(tài)替換三方包中的Class文件過程詳解的詳細(xì)內(nèi)容,更多關(guān)于編譯期動態(tài)替換Class文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)
這篇文章主要介紹了使用json字符串插入節(jié)點(diǎn)或者覆蓋節(jié)點(diǎn)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Java實(shí)現(xiàn)數(shù)據(jù)連接池Druid舉例
本文主要介紹了Java實(shí)現(xiàn)數(shù)據(jù)連接池Druid舉例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03