一、業(yè)務(wù)場景
傳統(tǒng)的SSM項目一般都為war包部署,多模塊的項目一般都是將模塊打包成jar包依賴進web工程中,但是對于作為基礎(chǔ)項目或者分模塊的web項目來說,打成war包對靜態(tài)資源的訪問就不太方便;這里介紹一下通過Maven WAR Plugin的<overlays>解決這個問題。這對于沒有上微服務(wù)的項目來說應(yīng)該是個不錯的解決方案,將通用的常規(guī)化的功能抽到base.war中,而其他類似項目依賴base.war作為基礎(chǔ)項目的一個模塊并對其做小部分定制化的修改即可,大大提高了同類型項目的交付周期。
二、overlay簡介
Overlays(覆蓋)主要用于跨多Web項目間共享公共資源。它能夠在目標(biāo)WAR本身覆蓋除了原生WAR構(gòu)件以外的所有文件,并在WEB-INF/lib目錄下收集原生WAR項目的依賴。
<overlay>元素包含有下列子元素:
id - overlay id。如果你不提供的話,WAR插件將自動生成一個。
groupId - 配置你想要覆蓋的groupId。
artifactId – 配置你想要覆蓋的構(gòu)件的artifactId。
type – 配置你想要覆蓋的構(gòu)件類型。默認值是:war。
classifier – 如果有多個構(gòu)件匹配當(dāng)前的groupId/artifactId,那么你需要配置構(gòu)件的classifier以明確覆蓋(classifier:該元素用來幫助定義構(gòu)建輸出的一些附屬構(gòu)件)。
includes - 要包含的文件。默認情況下,所有文件都能被包含。
excludes – 要排除的文件。默認情況下,在META – INF目錄是被排除在外的。
targetPath - 在webapp結(jié)構(gòu)的目標(biāo)相對路徑,當(dāng)然這只在覆蓋類型為war時才有效。默認情況下,覆蓋的內(nèi)容都追加在webapp的根節(jié)點下。
skip – 當(dāng)設(shè)置為true時,跳過本次覆蓋。默認值是:false。
三、實現(xiàn)方式
基礎(chǔ)項目:base.war,具體開發(fā)項目:a.war
如果在a項目中沒有對base項目種具體方法的引用,則只需要在a項目中添加下面的build節(jié)點。
<build>
<finalName>a</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<overlays>
<overlay>
<groupId>ff</groupId>
<artifactId>base</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
</plugins>
</build>
這樣便可以實現(xiàn)將base項目中同包同名的文件進行覆蓋,但是這也僅僅局限于靜態(tài)資源,如果我們需要引用Java的某各類,那么在打包的時候會發(fā)現(xiàn)出現(xiàn)找不到j(luò)ava類的報錯。
問題原因:
由于開發(fā)中我們只需要覆蓋部分文件,方法調(diào)用依然是調(diào)用base中定義的方法,但根據(jù)Java規(guī)范,classpath不能指定WAR文件。這就意味著在開發(fā)工具中調(diào)試時,項目可以正常運行;但是在編譯時,a項目無法訪問base項目中定義的類,所以在a項目中,我們不能像常規(guī)類組件那樣擴展或使用base定義的類。要解決這一問題,我們必須在base中重新設(shè)置maven-war-plugin的一項缺省配置,將classes打包成jar包引入到a中,這樣才能使得a編譯打包成功。相關(guān)pom配置。
在base項目中添加maven-war-plugin插件:
<build>
<finalName>base</finalName>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<!-- 把class打包jar作為附件 -->
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>
在a項目中引入上面打包的jar文件:
<dependencies>
<dependency>
<groupId>ff</groupId>
<artifactId>base</artifactId>
<version>1.0.0</version>
<type>jar</type>
<classifier>classes</classifier>
<scope>provided</scope>
</dependency>
</dependencies>
這里還要注意下scope不能隨意修改,這個jar我們只是編譯需要,不需要放到最終打包的war文件中,如果放入最終的war包中,啟動時Mybatis會報錯(具體原因是Mapper文件的namespace重名了,因為項目中有一份,jar包中還有一份)。
<scope>可以使用5個值:
- compile,缺省值,適用于所有階段,會隨著項目一起發(fā)布。
- provided,類似compile,期望JDK、容器或使用者會提供這個依賴。如servlet.jar。
- runtime,只在運行時使用,如JDBC驅(qū)動,適用運行和測試階段。
- test,只在測試時使用,用于編譯和運行測試代碼。不會隨項目發(fā)布。
- system,類似provided,需要顯式提供包含依賴的jar,Maven不會在Repository中查找它。
依賴范圍控制哪些依賴在哪些classpath 中可用,哪些依賴包含在一個應(yīng)用中。讓我們詳細看一下每一種范圍:
compile (編譯范圍)
compile是默認的范圍;如果沒有提供一個范圍,那該依賴的范圍就是編譯范圍。編譯范圍依賴在所有的classpath 中可用,同時它們也會被打包。
provided (已提供范圍)
provided 依賴只有在當(dāng)JDK 或者一個容器已提供該依賴之后才使用。例如, 如果你開發(fā)了一個web 應(yīng)用,你可能在編譯 classpath 中需要可用的Servlet API 來編譯一個servlet,但是你不會想要在打包好的WAR 中包含這個Servlet API;這個Servlet API JAR 由你的應(yīng)用服務(wù)器或者servlet 容器提供。已提供范圍的依賴在編譯classpath (不是運行時)可用。它們不是傳遞性的,也不會被打包。
runtime (運行時范圍)
runtime 依賴在運行和測試系統(tǒng)的時候需要,但在編譯的時候不需要。比如,你可能在編譯的時候只需要JDBC API JAR,而只有在運行的時候才需要JDBC
驅(qū)動實現(xiàn)。
test (測試范圍)
test范圍依賴 在一般的編譯和運行時都不需要,它們只有在測試編譯和測試運行階段可用。
system (系統(tǒng)范圍)
system范圍依賴與provided 類似,但是你必須顯式的提供一個對于本地系統(tǒng)中JAR 文件的路徑。這么做是為了允許基于本地對象編譯,而這些對象是系統(tǒng)類庫的一部分。這樣的構(gòu)件應(yīng)該是一直可用的,Maven 也不會在倉庫中去尋找它。如果你將一個依賴范圍設(shè)置成系統(tǒng)范圍,你必須同時提供一個 systemPath 元素。注意該范圍是不推薦使用的(你應(yīng)該一直盡量去從公共或定制的 Maven 倉庫中引用依賴)。