問題:
之前一直用的是mvn install 命令來構建項目,但是最近發現最新的代碼沒有在war包中。之前看的說 mvn install 命令會執行之前的所有階段,會被編譯,測試,打包。
經查最后采用mvn clean package -DskipTests=true -P prod (先清除,再打包,跳過測試,選擇prod生產環境的配置文件構建)
原因:
maven構建過程
mvn clean ----- 【清理】 將編譯(mvn compile)產生的【target】文件夾刪除掉,但是不會刪除本地的maven倉庫已經生成的jar文件。
mvn compile --- 【編譯】在項目根目錄生【target】目錄,里面包含【classes】文件夾,里面是已經編譯好的java類(.class文件)
mvn test-------- 【測試】會先生成target文件夾,里面有【classes】和【test-classes】文件夾,所 以執行mvn test命令時,會先編譯項目,在執行測試代碼
mvn package----【打包】看到項目的根目錄下【編譯】后生成的【target】文件夾中多了一個Hello-0.0.1-SNAPSHOT.jar,這個Hello-0.0.1-SNAPSHOT.jar就是打包成功之后Maven幫我們生成的jar文件,package命令會先執行編譯再執行打包
mvn install -----就是把maven構建項目的【清理clean】→【編譯】→【測試】→【打包】的這幾個過程都做了,同時將打包好的jar包發布到本地的Maven倉庫中,所以maven最常用的命令還是"mvn install",這個命令能做的事情最多,所以這個最常用
maven在執行一個生命周期的命令的是時候將會執行之前的所有生命周期操作,比如執行mvn install,會執行前面一系列的動作包括 compile , package , test 等,具體請查看maven的官方文檔。這個特性使maven的命令更加簡潔易用。
再來分析原來的問題,為什么修改的內容不生效,肯定是最終打出來的war包中的內容沒有更新,而war包中會依賴其他子工程的jar包,如果jar 包沒有更新過,那war包調用老的jar包也會導致新內容不生效。定位到問題的原因應該是jar包沒有用最新的資源(java或者配置文件),那jar包 又是什么時候,誰去打的呢。
上面我們提到我們執行mvn install的時候會先執行mvn package,maven就是通過這個生命周期來根據用戶配置,進行打包(war、jar或者其他),這會在每個工程 pom.xml 文件中設置,類似如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ... <packaging>war</packaging> ... </project>
這里指定package的時候打成一個war包,改成jar,就會被打成jar包。
我們看jar形式的情況,mvn package 會調用 maven-jar-plugin 這個插件進行打包。
下面我們做一些實驗來看這個插件打包的時候的情況
1. 修改target目錄下打好的jar包中class以及配置文件的內容,在運行命令mvn package
,結果target包中的內容沒有被覆蓋。
2. 修改源代碼中的內容,再運行命令mvn package
,結果target包中的內容被覆蓋了,產生了新的包。
3. 修改target目錄下打好的jar包中的內容,運行命令mvn package -Djar.forceCreation
,這個參數應該是強制創建jar包,所以結果target中的jar包內容被覆蓋了,產生了新的jar包。
根據上面的實驗好像還是不能解釋什么時候應該用clean將target下面的內容刪除重新生成,jar包,不過至少是明白了一些規則。
下面我們還是去看看 maven-jar-plugin
的源碼吧。
之前,我提一點,maven的debugg信息非常完備,需要查看debug信息只要在命令后面添加 -X 參數即可,如:mvn clean package -X
就能看到非常豐富的DEBUG信息。
回來,我們發現 org.codehaus.plexus.archiver.AbstractArchiver
中的關鍵一段,用來判斷是否強制新建jar
protected boolean checkForced() throws ArchiverException { if ( !isForced() && isSupportingForced() && isUptodate() ) { getLogger().debug( "Archive " + getDestFile() + " is uptodate." ); return false; } return true; }
這個方法是校驗是否強制重新創建jar包,只有當
1. 沒有將 jar.forceCreation 參數設為true
2. 並且支持強制設置
3. up to date,意思就是被認為是最新的內容,沒有改動
這個時候maven不進行新包的生成直接返回。
protected void execute() throws ArchiverException, IOException { if ( ! checkForced() ) { return; } if ( doubleFilePass ) { skipWriting = true; createArchiveMain(); skipWriting = false; createArchiveMain(); } else { createArchiveMain(); } finalizeZipOutputStream( zOut ); }
所以除了那個強制的參數以外,就是看什么時候 isUptodate 為true,查看關鍵代碼:
protected boolean isUptodate() throws ArchiverException { final File zipFile = getDestFile(); final long destTimestamp = zipFile.lastModified(); if ( destTimestamp == 0 ) { getLogger().debug( "isUp2date: false (Destination " + zipFile.getPath() + " not found.)" ); return false; // File doesn't yet exist } final Iterator it = resources.iterator(); if ( !it.hasNext() ) { getLogger().debug( "isUp2date: false (No input files.)" ); return false; // No timestamp to compare } while ( it.hasNext() ) { final Object o = it.next(); final long l; if ( o instanceof ArchiveEntry ) { l = ( (ArchiveEntry) o ).getResource() .getLastModified(); } else if ( o instanceof PlexusIoResourceCollection ) { try { l = ( (PlexusIoResourceCollection) o ).getLastModified(); } catch ( final IOException e ) { throw new ArchiverException( e.getMessage(), e ); } } else { throw new IllegalStateException( "Invalid object type: " + o.getClass() .getName() ); } if ( l == PlexusIoResource.UNKNOWN_MODIFICATION_DATE ) { // Don't know what to do. Safe thing is to assume not up2date. getLogger().debug( "isUp2date: false (Resource with unknown modification date found.)" ); return false; } if ( l > destTimestamp ) { getLogger().debug( "isUp2date: false (Resource with newer modification date found.)" ); return false; } } getLogger().debug( "isUp2date: true" ); return true; }
代碼中提到有這么幾個情況,會認為jar包不是最新的:
1. jar包不存在(其實就是mvn clean的效果)
2. 傳入比較的文件資源不存在
3. Resource with unknown modification date found,資源的修改時間未知
4. Resource with newer modification date found,jar包的最后修改時間比資源的最后修改時間早
總結
1. 理論上來講不做mvn clean 得到的jar包應該是最新的,除非其他方式修改jar包中的內容而不修改源代碼。
2. 平時可以用mvn install,而不進行chean節省時間(如果你覺得節省時間多的話),但最保險還是用 mvn clean install 生成最新的jar包或其他包
3. 不想用mvn clean又想保證jar包最新,建議添加 -Djar.forceCreation
參數
jar-plugin源代碼地址:http://svn.apache.org/repos/asf/maven/plugins/tags/maven-jar-plugin-2.4
參考:https://blog.csdn.net/abc86319253/article/details/44019881