1. 程式人生 > >增加一個bean改變spring初始化順序問題

增加一個bean改變spring初始化順序問題

    工程中有2個bean,A和B,其中必須先初始化A再初始化B,但是沒有depend-on或者Order等方式去保證,只不過恰好剛好這麼執行著沒出事,但是突然增加了一個C之後,就先初始化B再初始化A導致問題,但是在主幹版本上卻沒問題。

    解決這個問題其實很簡單,depend-on即可,但是為什麼會分支版本上會增加C後就改變AB的初始化順序?為什麼主幹版本上同樣新增沒問題呢?可以看spring的原始碼 DefaultListableBeanFactory 類中有

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

這個map儲存的就是xml中定義的bean結構,spring解析定義的bean之後,儲存到這裡,其中key為beanid,value為bean的詳細資訊,根據beanid算出hashCode分別為1505068002,1682286698,從定義可以看到這是一個ConcurrentHashMap,在獲取到定義後,spring在初始化的時候是怎麼初始化的呢?顯然也是從這個定義裡取值,為了簡化問題,我們理解為是類似如下方式

for(Entry<String,BeanDefinition> entry : beanDefinitionMap)

去遍歷bean然後根據BeanDefinition來初始化,考慮分支版本的是用JDK1.6,主幹是用JDK1.8,不同ConcurrentHashMap的實現是否會導致取出來的順序不一樣呢?

    JDK1.8中,ConcurrentHashMap 的實現是這樣的,陣列Node<K,V>[]table,每個元素為一個Node,每個元素直接落到Node上,去追加連結串列或者在紅黑樹上,總之是一次hash

    JDK1.6中,ConcurrentHashMap 的實現是這樣的,先Segment<K,V>[]segments來進行一個分段,然後每個Segment裡再包含元素 HashEntry<K,V>[] table,即,會對每個元素會有2次hash,第一次定位到Segment,第二次在Segment內部定位到自己的位置userService

 可以知道,在JDK1.8中2個bean的位置是固定的(所以主幹版本同樣新增但是AB初始化順序不變),但是在JDK1.6中可能會發生這種情況:B在第一個Segment中,A在第二個Segment中,導致取出來的時候先獲取到B,後取出來A,所以出現了空指標,同樣,也可以解釋,為什麼新增了1個id為C的bean就導致了問題,但是之前沒問題,因為新增bean導致ConcurrentHashMap中部分bean所在的Segment發生變化。

    當然,對有依賴關係顯然是顯式指定出來的好,不然像這樣坑後來人就不好了。