編譯期動(dòng)態(tài)替換三方包中的Class文件過(guò)程詳解
背景
最近做業(yè)務(wù)時(shí)遇到一個(gè)問(wèn)題,客戶(hù)想在底層數(shù)據(jù)添加一個(gè)字段,只能乖乖的添加表字段、實(shí)體添加對(duì)應(yīng)屬性,一切都在預(yù)期中進(jìn)行這,但是這個(gè)工程是經(jīng)過(guò)二開(kāi)的,展示層實(shí)體沒(méi)法直接添加,于是想當(dāng)然繼承實(shí)體擴(kuò)展字段,沒(méi)想到頂層一堆Request、Response,如果一個(gè)一個(gè)進(jìn)行擴(kuò)展馬也得累死,于是就思考有沒(méi)有簡(jiǎn)便的方法僅對(duì)目標(biāo)實(shí)體進(jìn)行操作來(lái)完成字段添加的方法。
思考過(guò)程
在Java中要在類(lèi)中添加字段屬性,除了顯示編碼外,還有一種技術(shù)就是編譯期間動(dòng)態(tài)修改,比如Lombok、Mapstruct等都是在編譯期動(dòng)態(tài)生成代碼,提高編碼效率,所以我也考慮通過(guò)這種方式編譯期添加目標(biāo)字段屬性,百度了一通沒(méi)有合適的方式動(dòng)態(tài)添加,但是手寫(xiě)通過(guò)字節(jié)碼注入一定是可以實(shí)現(xiàn)的,想想成本還是有點(diǎn)高,趕緊轉(zhuǎn)換思路,既然不能動(dòng)態(tài)插入字段,那能不能直接替換目標(biāo)類(lèi)呢?一想到這就有戲,在Java中加載類(lèi)是通過(guò)類(lèi)加載器進(jìn)行加載的,有了依據(jù)后趕緊接著百度,果不然讓我發(fā)現(xiàn)一種方式,通過(guò)maven插件的方式實(shí)現(xiàn),客官接著往下看。
一般情況下不建議用這么hack的方式哈,盡量保持三方包的新鮮度,避免未來(lái)升級(jí)導(dǎo)致的兼容性問(wèn)題。
主角出場(chǎng)
主角:maven-dependency-plugin
這僅僅是處理的一種方式,大家如果有更好的處理方式,可以放到評(píng)論區(qū),我們一起討論,互相學(xué)習(xí)進(jìn)步。
實(shí)現(xiàn)原理
通過(guò)配置maven-dependency-plugin, 可以將我們指定的dependency解壓到項(xiàng)目的class目錄中,設(shè)置不覆蓋本地項(xiàng)目相同class文件(類(lèi)的全限定名相同),就實(shí)現(xiàn)了本地文件替換三方j(luò)ar中類(lèi)文件的目的了。
在Java應(yīng)用中,如果存在多個(gè)同名類(lèi),最終只會(huì)加載一個(gè)目標(biāo)類(lèi),到底會(huì)加載哪個(gè)同名類(lèi)是又類(lèi)加載器的雙親委派機(jī)制決定的,先請(qǐng)求父類(lèi)加載器加載,父類(lèi)無(wú)法加載回到應(yīng)用程序加載器,應(yīng)用程序無(wú)法加載就會(huì)到類(lèi)路徑下即class目錄中加載,如果仍然不存在會(huì)到依賴(lài)中進(jìn)行加載。
基于以上原理,實(shí)現(xiàn)類(lèi)文件覆蓋就有了依據(jù),那么接下來(lái)具體實(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)識(shí)符,unpack是將依賴(lài)從倉(cāng)庫(kù)中解壓到指定目錄 -->
<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,依賴(lài)解壓到目錄時(shí)不會(huì)進(jìn)行覆蓋,設(shè)置為true則會(huì)覆蓋 -->
<overWrite>false</overWrite>
<!-- 目標(biāo)class文件輸出目錄 -->
<outputDirectory>${project.build.directory}/classes</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
第二步:編譯工程

這一步還沒(méi)有定義目標(biāo)類(lèi),暫時(shí)僅是將目標(biāo)三方包的類(lèi)放到了class目錄下,標(biāo)紅的位置是即將要進(jìn)行修改的位置。
第三步:添加目標(biāo)類(lèi)

- 包名和目標(biāo)類(lèi)所在包名完全一致;
- 類(lèi)名保持一致;
第四步:重新編譯工程

至此已經(jīng)能夠看到效果了,整個(gè)方式過(guò)程也比較簡(jiǎn)單,去掉中間的編譯過(guò)程,總共兩步。
總結(jié)
這種Hack的方式在業(yè)務(wù)編碼中建議少用,通過(guò)上邊的方式雖然能解決問(wèn)題,但同時(shí)也引入了一些副作用,一方面相當(dāng)于依賴(lài)包引入兩份,另一方面當(dāng)依賴(lài)包升級(jí)時(shí)可能存在疏漏??纯磳W(xué)習(xí)學(xué)習(xí),多一種解決方式多一條路,希望大家每天編碼順順利,我就先溜了!
以上就是編譯期動(dòng)態(tài)替換三方包中的Class文件過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于編譯期動(dòng)態(tài)替換Class文件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java8實(shí)現(xiàn)對(duì)List<Integer>的求和
這篇文章主要介紹了Java8實(shí)現(xiàn)對(duì)List<Integer>的求和方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況
這篇文章主要介紹了mac下修改idea的jvm運(yùn)行參數(shù)解決idea卡頓的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
mybatis-plus中更新null值的問(wèn)題解決
本文主要介紹 mybatis-plus 中常使用的 update 相關(guān)方法的區(qū)別,以及更新 null 的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-04-04
解決@Transaction注解導(dǎo)致動(dòng)態(tài)切換更改數(shù)據(jù)庫(kù)失效問(wèn)題
這篇文章主要介紹了解決@Transaction注解導(dǎo)致動(dòng)態(tài)切換更改數(shù)據(jù)庫(kù)失效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java實(shí)現(xiàn)輸出數(shù)字三角形實(shí)例代碼
大家好,本篇文章主要講的是Java實(shí)現(xiàn)輸出三角形實(shí)例代碼,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下,方便下次瀏覽2022-01-01
Spring Boot線(xiàn)程池使用的一些實(shí)用心得
理論上線(xiàn)程越多程序可能更快,但在實(shí)際使用中我們需要考慮到線(xiàn)程本身的創(chuàng)建以及銷(xiāo)毀的資源消耗,以及保護(hù)操作系統(tǒng)本身的目的我們通常需要將線(xiàn)程限制在一定的范圍之類(lèi),這篇文章主要給大家介紹了關(guān)于Spring Boot線(xiàn)程池使用的一些實(shí)用心得,需要的朋友可以參考下2021-09-09
servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié))
在利用servlet或Filter進(jìn)行url請(qǐng)求的匹配時(shí),很關(guān)鍵的一點(diǎn)就是匹配規(guī)則。這篇文章主要介紹了servlet的url-pattern匹配規(guī)則詳細(xì)描述(小結(jié)),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-07-07

