避免多個(gè)jar通過maven打包導(dǎo)致同名配置文件覆蓋沖突問題
前言
不知道大家在開發(fā)的過程中,有沒有遇到這種場景,外部的項(xiàng)目想訪問內(nèi)部nexus私倉的jar,因?yàn)樗絺}不對外開放,導(dǎo)致外部的項(xiàng)目沒法下載到私倉的jar,導(dǎo)致項(xiàng)目因缺少jar而無法運(yùn)行。
通常遇到這種場景,常用的解法有,外部項(xiàng)目跟內(nèi)部nexus的網(wǎng)絡(luò)打通,比如通過VPN?;蛘邔⑺絺}的jar直接下載下來給到外部項(xiàng)目。對于第二種方案有時(shí)候因?yàn)樗絺}的jar里面有依賴其他的內(nèi)部jar,導(dǎo)致要下載多個(gè)jar的情況。這時(shí)候?yàn)榱朔奖?,我們可能會將這些jar合并成一個(gè)大jar,再給出去。而目前有些jar都是一些starter,會有一些同名的配置文件,比如spring.factories。如果不進(jìn)行處理,直接打包,就會出現(xiàn)同名配置文件覆蓋的情況
本文就是要來聊聊當(dāng)多個(gè)jar合并成一個(gè)jar,如何解決多個(gè)同名配置文件覆蓋的情況
解決思路
通過maven-shade-plugin這個(gè)插件,利用插件的org.apache.maven.plugins.shade.resource.AppendingTransformer來處理處理多個(gè)jar包中存在重名的配置文件的合并。他的核心是在于合并多個(gè)同名配置文件內(nèi)容,而非覆蓋
示例配置如下
<build> <plugins> <!-- 防止同名配置文件,在打包時(shí)被覆蓋,用來處理多個(gè)jar包中存在重名的配置文件的合并 參考dubbo:https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.factories</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.handlers</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.schemas</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.tooling</resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin>
打包后的配置文件的效果如下圖
眼尖的朋友應(yīng)該發(fā)現(xiàn)了,同名的配置內(nèi)容是通過追加的方式,但僅僅追加,其實(shí)有時(shí)候還滿足不了要求,比如spring.factories文件,他需要達(dá)到的效果應(yīng)該是如下圖
后面我通過maven-shade-plugin的官方示例(https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html)試圖想找到解決方案,但是有點(diǎn)遺憾,沒找到。
于是在我面前就有兩條路,一條是放棄maven-shade-plugin插件,比如選擇其他類似的插件,比如maven-assembly-plugin,這種方案我試過,發(fā)現(xiàn)maven-assembly-plugin這個(gè)插件的擴(kuò)展配置,比maven-shade-plugin復(fù)雜一些,于是放棄。最后選擇了在maven-shade-plugin基礎(chǔ)再擴(kuò)展一下。
擴(kuò)展的思路
我并沒采用直接修改maven-shade-plugin插件的方式,而是在maven-shade-plugin打包后的基礎(chǔ)上,再進(jìn)行插件定制。實(shí)現(xiàn)的思路也不難,就是修改maven-shade-plugin打成jar后的spring.factories文件內(nèi)容,將
調(diào)整成形如下即可
自定義maven插件spring-factories-merge-plugin
核心思路
1、如何讀取配置文件spring.factories中key重復(fù)的內(nèi)容,而不被覆蓋
如果是直接使java.util.properties的讀取,當(dāng)配置文件中有key重復(fù)時(shí),比如有多個(gè)org.springframework.boot.autoconfigure.EnableAutoConfiguration時(shí),最后會出現(xiàn)value值被覆蓋的情況。
解決方案,我們可以利用org.apacche.commons.configuration.PropertiesConfiguration來進(jìn)行處理
在項(xiàng)目的pom引入GAV
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-configuration2</artifactId> <version>${commons-configuration2}</version> </dependency>
讀取配置示例代碼
@SneakyThrows public static Map<String, Set<String>> readFactoriesFile(InputStream input) { // 讀取 spring.factories 內(nèi)容 //利用PropertiesConfiguration取配置文件中key重復(fù)的內(nèi)容,而不被覆蓋 PropertiesConfiguration properties = new PropertiesConfiguration(); properties.read(new InputStreamReader(input)); Map<String, Set<String>> multiSetMap = new LinkedHashMap<>(); Iterator<String> keys = properties.getKeys(); while(keys.hasNext()) { String key = keys.next(); String[] values = properties.getStringArray(key); Set<String> collectSet = new LinkedHashSet<>(); buildKeyValues(values, collectSet); multiSetMap.put(key,collectSet); } return multiSetMap; }
2、如何將修改后的配置文件,重新寫入jar
我這邊的思路就是直接利用IO進(jìn)行操作了
示例如下
public static void writeFactoriesFile(String factoriesBaseClassPathDir,String finalJarName) throws IOException { String jarFilePath = String.format(factoriesBaseClassPathDir + "/target/" + finalJarName).replace("\\", "/").replaceAll("http://+", "/"); if(!jarFilePath.endsWith(".jar")){ jarFilePath = jarFilePath + ".jar"; } JarFile jarFile = new JarFile(jarFilePath); if(jarFile != null){ List<JarEntry> jarFiles = jarFile.stream().collect(Collectors.toList()); @ Cleanup FileOutputStream fos = new FileOutputStream(jarFile.getName(), true); @ Cleanup JarOutputStream jos = new JarOutputStream(fos); for (JarEntry jarEntry : jarFiles) { if(jarEntry.getName().startsWith(SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION)){ try { @ Cleanup InputStream input = jarFile.getInputStream(jarEntry); Map<String, Set<String>> factoriesMap = readFactoriesFile(input); jos.putNextEntry(new JarEntry(jarEntry.getName())); generateFactoriesContent(factoriesMap,jos); } catch (IOException e) { e.printStackTrace(); } }else{ //表示將該JarEntry寫入jar文件中 也就是創(chuàng)建該文件夾和文件 jos.putNextEntry(new JarEntry(jarEntry)); jos.write(streamToByte(jarFile.getInputStream(jarEntry))); } } } }
項(xiàng)目中如何配置插件
<build> <plugins> <!-- 防止同名配置文件,在打包時(shí)被覆蓋,用來處理多個(gè)jar包中存在重名的配置文件的合并 參考dubbo:https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.factories</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.handlers</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.schemas</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/spring.tooling</resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin> <plugin> <groupId>com.github.lybgeek.jar</groupId> <artifactId>spring-factories-merge-plugin</artifactId> <version>0.0.1-SNAPSHOT</version> <executions> <execution> <phase>package</phase> <goals> <goal>springFactoriesMerge</goal> </goals> </execution> </executions> <configuration> <factoriesBaseClassPathDir>${basedir}</factoriesBaseClassPathDir> <finalJarName>${project.artifactId}-${project.version}</finalJarName> </configuration> </plugin> </plugins> </build>
這邊有個(gè)小細(xì)節(jié)是當(dāng)maven-shade-plugin和spring-factories-merge-plugin的執(zhí)行生命周期都是相同階段,比如都是在package時(shí),則maven-shade-plugin放置順序得在spring-factories-merge-plugin之前,因?yàn)閟pring-factories-merge-plugin是對maven-shade-plugin打包后的結(jié)果進(jìn)行二次加工。如果maven-shade-plugin不放置順序得在spring-factories-merge-plugin之前,則spring-factories-merge-plugin的執(zhí)行階段就要比maven-shade-plugin靠后,比如maven-shade-plugin在package階段執(zhí)行,則spring-factories-merge-plugin就得在install或者deploy階段執(zhí)行
打包后的效果圖如下
總結(jié)
之前在看開源框架的時(shí)候,很經(jīng)常都是聚焦在源碼上,而不會去注意一些maven插件,這次因?yàn)橛羞@打jar的需求。我發(fā)現(xiàn)不管是springboot還是dubbo本身就集成一些寶藏插件,比如這個(gè)maven-shade-plugin插件,我就是dubbo那邊找到的,地址在
https://github.com/apache/dubbo/blob/master/dubbo-all/pom.xml。比如版本占位符插件flatten-maven-plugin在dubbo和springboot都有看到使用。如果后面有對maven插件由需求,推薦可以從springboot或者dubbo那邊去搜,估計(jì)會有意想不到的收獲
demo鏈接 https://github.com/lyb-geek/springboot-learning/tree/master/springboot-jar-merge
以上就是避免多個(gè)jar通過maven打包導(dǎo)致同名配置文件覆蓋沖突問題的詳細(xì)內(nèi)容,更多關(guān)于maven打包jar同名沖突的資料請關(guān)注腳本之家其它相關(guān)文章!
- maven打包本地jar到項(xiàng)目中的方法實(shí)現(xiàn)
- maven打包所有依賴對外提供sdk.jar
- Maven打包所有依賴到一個(gè)可執(zhí)行jar中遇到的問題
- maven工程打包引入本地jar包的實(shí)現(xiàn)
- IDEA在SpringBoot項(xiàng)目使用Maven打包后jar包太小問題及解決
- idea本地jar使用maven打包本地依賴實(shí)現(xiàn)自動編譯到項(xiàng)目里的操作
- maven打包加入本地jar包的實(shí)現(xiàn)
- maven多模塊項(xiàng)目單獨(dú)打包指定模塊jar包方式
- Maven項(xiàng)目打包為jar的四種方式
相關(guān)文章
SpringSecurity OAuth2單點(diǎn)登錄和登出的實(shí)現(xiàn)
本文主要介紹了SpringSecurity OAuth2單點(diǎn)登錄和登出的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Java動態(tài)規(guī)劃之丑數(shù)問題實(shí)例講解
這篇文章主要介紹了Java動態(tài)規(guī)劃之丑數(shù)問題實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-09-09使用EasyPOI實(shí)現(xiàn)多sheet動態(tài)列導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了如何使用EasyPOI根據(jù)指定時(shí)間范圍創(chuàng)建動態(tài)列,以及如何將數(shù)據(jù)組織成符合要求的格式并導(dǎo)出,感興趣的可以了解下2025-03-03SpringBoot獲取ApplicationContext的3種方式
這篇文章主要為大家詳細(xì)介紹了SpringBoot獲取ApplicationContext的3種方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09BeanUtils.copyProperties()屬性名相同但是類型不同問題
這篇文章主要介紹了BeanUtils.copyProperties()屬性名相同但是類型不同問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-09-09Springcloud-nacos實(shí)現(xiàn)配置和注冊中心的方法
這篇文章主要介紹了Springcloud-nacos實(shí)現(xiàn)配置和注冊中心的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07Java 多線程Synchronized和Lock的區(qū)別
這篇文章主要介紹了Java 多線程Synchronized和Lock的區(qū)別,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01Java接口的作用_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Java接口的作用,涉及到接口的規(guī)范相關(guān)知識,需要的的朋友參考下2017-04-04