最近由於不清楚maven(2.2.x)增量編譯的機制,導致應用出現了一個當時覺得非常詭異的一個問題。先描述一下問題。
背景是應用A有一個公用的base包,版本為1.6.6-SNAPSHOT,應用B依賴於這個公用的base包。我在base包中修改了一個字符串變量的值,該變量是一個緩存的key(如下面代碼的Constants類,中的CACHE_KEY)。然后使用mvn deploy 命令把base包上傳到中心庫中。出現的問題是應用B打包部署,應用上線后,使用后台功能刪除"cache.key.new"對應的緩存值,提示刪除成功。但是前台展示的還是老的值(前台展示取的數據是從緩存取出的,簡化代碼后,參考下面的CategoryManager的showCategory()方法,根據key在緩存中取出值,然后前台展示)。
public Interface Constants{ public interface Cache{ String CACHE_KEY = “cache.key.new”;//舊值為"cache.key" } }
public Class CategoryManager{ private static Map<int, String> keyMaps = new HashMap<String, String>(); static { keyMaps.put(1, Constants. Cache. CACHE_KEY);//把緩存的key存到map中 ......... } public Object showCategory(){ return cacheManager.get(keyMaps.get(1));//在緩存中獲取數據 } }
一開始懷疑是包沒有成功deploy到中心庫中,然后在中心庫中把1.6.6-SNAPSHOT的源碼包拉了下來,發現里面的代碼是新的。
我的印象中deploy是maven生命周期的最后一步,執行deploy命令是會執行compile編譯操作的,當時懷疑自己記錯了,以為base包deploy的時候沒有走到compile,就把包發到中心庫中了,后續把base包的1.6.6-SNAPSHOT的編譯后的文件拉下來,並且反編譯Constants類,發現CACHE_KEY的確是新的值"cache.key.new",證明這塊是被重新編譯過了。(后續翻閱了下資料”
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html maven生命周期“發現自己記的的確沒錯)
最后把CategoryManager.class反編譯過來一看,發現里面的部分代碼是這樣的:
public Class CategoryManager{ private static Map<int, String> keyMaps = new HashMap<String, String>(); static { keyMaps.put(1, "cache.key");//問題出在這里,這個"cache.key"沒有替換成新的值"cache.key.new" ......... } ........ }
發現問題了,這里的"cache.key"並沒有替換成新值。也就是說CategoryManager類並沒有重新編譯。這里的原因是maven的增量編譯機制。
后續找了些資料表明,默認情況下maven是增量編譯的,maven在編譯的時候會先去比較源文件(.java)文件的修改時間和對應類文件(.class)的修改時間,如果源文件的修改時間比類文件的修改時間要晚的話,重新編譯原文件否則不做處理(另外,如果刪除了一個java文件,對應的class文件在增量編譯時是不會被移除的)。
在這里CategoryManager的源文件並沒有發生過改變,所以不會重新編譯。並且這個地方恰好是靜態代碼塊,這段代碼 keyMaps.put(1, Constants. Cache. CACHE_KEY); 編譯時直接被替換成 keyMaps.put(1, "cache.key"); ,如果是在非靜態代碼塊里面使用的話,這塊地方是不會出問題的。所以之前一直使用mvn deploy都沒有出現過問題。
后續找了些資料表明,默認情況下maven是增量編譯的,maven在編譯的時候會先去比較源文件(.java)文件的修改時間和對應類文件(.class)的修改時間,如果源文件的修改時間比類文件的修改時間要晚的話,重新編譯原文件否則不做處理(另外,如果刪除了一個java文件,對應的class文件在增量編譯時是不會被移除的)。
在這里CategoryManager的源文件並沒有發生過改變,所以不會重新編譯。並且這個地方恰好是靜態代碼塊,這段代碼 keyMaps.put(1, Constants. Cache. CACHE_KEY); 編譯時直接被替換成 keyMaps.put(1, "cache.key"); ,如果是在非靜態代碼塊里面使用的話,這塊地方是不會出問題的。所以之前一直使用mvn deploy都沒有出現過問題。
那要怎么避免這個問題呢,使用mvn clean deploy。clean會做一些清理的工作,包括移除上一次構建的文件。這樣就能很好的解決這個問題了。
參考:
http://blog.jetbrains.com/teamcity/2012/03/incremental-building-with-maven-and-teamcity/ 部分有關於maven的 incremental building