<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
上篇文章介紹了Bean元資訊的設定與解析過程,限於篇幅Bean註冊過程就沒展開。
這裡主要圍繞BeanDefinitionReaderUtils#registerBeanDefinition
展開分析下Bean註冊過程
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
上面無論是註冊bd還是建立alias-beanName之間的關係,均用到了BeanDefinitionRegistry
,因此我們就以它為突破口來展開
對圖中常用介面或類進行說明:
ListableBeanFactory
集合型別BeanFactory 提供一種可以查詢所有Bean範例的能力
getBeanNamesForType(Class)
根據型別去查詢Bean名稱列表不會強制Bean的初始化,可從原始碼中看出來getBeansOfType(Class)
根據型別去查詢Bean範例列表,會強制Bean的初始化,可從原始碼中看出來getBeanNamesForAnnotation(Class)
根據註解型別獲取Bean名稱列表getBeansWithAnnotation(Class)
根據註解型別獲取Bean範例列表findAnnotationOnBean(String,Class)
根據指定名稱+標註型別獲取Bean範例Hierarchical([ˌhaɪəˈrɑːkɪkl])BeanFactory
層次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory
設定其父容器getParentBeanFactory()
獲取父容器boolean containsLocalBean(String name)
在當前容器中查詢是否存在該名稱的Bean範例SingletonBeanRegistry
單範例BeanFactory
,與單範例有關ConfigurableBeanFactory
可設定的BeanFactory
,這個一般不用於應用程式,是給其他BeanFactory擴充套件用的。的確,定義了很多設定方法ConfigurableListableBeanFactory
可設定的集合型別的BeanFactoryAutowireCapableBeanFactory
提供具有自動裝配能力的BeanFactory透過繼承體系可以看出,BeanDefinitionRegistry
的實現類是DefaultListableBeanFactory
,該類同時實現了諸多介面,可謂是BeanFactory中集大成者,因此我們到DefaultListableBeanFactory
中閱讀下bd註冊及別名註冊的原始碼
先來分析下DefaultListableBeanFactory
的幾個重要的成員屬性
// 這個實質上就是IoC容器中Bean的載體,沒錯 它很重要,但它是無序的 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); //它代表了bd名稱的集合,它是有序的 遵循bd註冊的順序 private volatile List<String> beanDefinitionNames = new ArrayList<>(256); // 這是已建立bd名稱的集合,在doGetBean方法根據beanName建立Bean時,beanName會被加到此集合中 private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
上面兩個屬性都比較重要,兩者結合使用的話可以實現bd的順序存取(其實就是遍歷beanDefinitionNames集合時,使用beanDefinitionMap去獲取bd)
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //對beanName、bd進行非空驗證 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //如果bd是AbstractBeanDefinition型別,則對bd進行驗證(一般情況下 我們場景的bd都是繼承自AbstractBeanDefinition的) if (beanDefinition instanceof AbstractBeanDefinition) { try { //bd驗證 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { //省略異常程式碼 } } //從beanDefinitionMap根據beanName取bd BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //如果beanName名稱的bd已經存在 if (existingDefinition != null) { //如果不允許Bean被重新註冊 則丟擲異常,這裡預設值是true if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } //如果已被註冊bd的角色值小於當前待註冊bd的角色值 else if (existingDefinition.getRole() < beanDefinition.getRole()) { // 省略紀錄檔輸出 } //如果已註冊的同名bd 與本次註冊的bd不相同 else if (!beanDefinition.equals(existingDefinition)) { //省略紀錄檔輸出 } else { //省略紀錄檔輸出 } //將beanName-bd鍵值對放入beanDefinitionMap集合 this.beanDefinitionMap.put(beanName, beanDefinition); } else { //流程走到這裡 說明在beanDefinitionMap中不存在同名bd //條件成立 說明alreadyCreated不為空 即有bd已被建立 if (hasBeanCreationStarted()) { // 如果在此之間 有bean正在被建立 則這裡進行加鎖處理 synchronized (this.beanDefinitionMap) { //將beanName-bd鍵值對放入beanDefinitionMap集合 this.beanDefinitionMap.put(beanName, beanDefinition); //將beanName新增到beanDefinitionNames集合中 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; //如果beanName是手動註冊的單例Bean名稱,則更新manualSingletonNames if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); //這裡從集合中刪除的原因個人理解: //manualSingletonNames記錄的是在registerSingleton時被新增的單範例beanName,而這裡注入的不是單範例Bean。因為manualSingletonNames包含了此beanName,因此需要剔除 updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { //如果沒有bean在被建立 //將beanName-bd鍵值對放入beanDefinitionMap集合 this.beanDefinitionMap.put(beanName, beanDefinition); //將beanName新增到集合中 this.beanDefinitionNames.add(beanName); //這裡從manualSingletonNames中剔除,個人理解為拖地操作,畢竟若集合中沒有此beanName 也remove不了 this.manualSingletonNames.remove(beanName); } //這個集合表示凍結設定時快取的beanName集合,暫時未理解透此集合的用途 this.frozenBeanDefinitionNames = null; } //如果已存在同名bd或已存在同名的單例物件,則重置所有已被快取的同名的bd資料,因此這裡bd註冊成功後,肯定後續還會再生成Bean的 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
其實分析下來發現Bean註冊的過程還是比較容易理解的,下面試著總結一下:
下面看一下清除bd資訊的程式碼邏輯
protected void resetBeanDefinition(String beanName) { // 如果此bd屬於被合併的BeanDefinition,則這裡將其從MergeBeanDefinition集合中剔除 clearMergedBeanDefinition(beanName); // 如果已存在同名的單例物件 則銷燬,具體細節先不展開 destroySingleton(beanName); // 這裡for迴圈邏輯與MergeBeanDefinition相關,如果存在MergedBeanDefinitionPostProcessor,則重置此bd for (BeanPostProcessor processor : getBeanPostProcessors()) { if (processor instanceof MergedBeanDefinitionPostProcessor) { ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); } } // BeanDefinition執行有層級的,如果此bd擁有多個父級bd,那麼這裡遞迴地重置其父bd for (String bdName : this.beanDefinitionNames) { if (!beanName.equals(bdName)) { BeanDefinition bd = this.beanDefinitionMap.get(bdName); if (beanName.equals(bd.getParentName())) { resetBeanDefinition(bdName); } } } }
看了Bean的註冊,再來看別名的註冊 發現流程比較清晰,基本上一目瞭然。
//注意 這裡的name 不要具象為beanName,雖然我們是從建立beanName--alias關係出發追溯到這裡的 public void registerAlias(String name, String alias) { //對name、alias進行斷言驗證 Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { //如果別名與beanName相同,那別名就沒有必要存在了,因此選擇直接從this.aliasMap中移除此別名 if (alias.equals(name)) { this.aliasMap.remove(alias); //省略紀錄檔輸出 } else { //從aliasMap中根據別名獲取name String registeredName = this.aliasMap.get(alias); if (registeredName != null) { //如果已存在的registeredName與此此要註冊的name一致,那就沒必要註冊了 if (registeredName.equals(name)) { return; } //流程走到這裡,說明同一個別名,對應兩個name,如果不允許alias覆蓋 則丟擲異常 if (!allowAliasOverriding()) { //省略異常及紀錄檔輸出 } //這裡對alias進行迴圈檢查,避免出現A的別名是B,B的別名是A的情況 checkForAliasCircle(name, alias); //將alias--name 放入aliasMap this.aliasMap.put(alias, name); //省略紀錄檔輸出 } } }
alias與beanName的對映關係,為根據名稱查詢Bean又提供了一種思路。就是說除了根據beanName外,也可以根據alias去查詢Bean。
這部分原始碼如下
//name可以是beanName,也可以是alias public String canonicalName(String name) { //區域性變數賦值 String canonicalName = name; // Handle aliasing... String resolvedName; do { //如果從aliasMap中能根據alias分析出beanName resolvedName = this.aliasMap.get(canonicalName); if (resolvedName != null) { canonicalName = resolvedName; } } while (resolvedName != null); // 無論入參name是beanName還是alias,這裡返回的都應該是beanName了 return canonicalName; }
好了,這篇主要分析了BeanDefinition的註冊,順帶著也說了別名的註冊情況。既然BeanDefinition已經註冊完成,那緊接著就是BeanDefinition的範例化過程了,這個放到下次分析吧。
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45