您好,登錄后才能下訂單哦!
這篇文章主要介紹“Spring Bean中Bean的注冊(cè)是什么”,在日常操作中,相信很多人在Spring Bean中Bean的注冊(cè)是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Spring Bean中Bean的注冊(cè)是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
這里主要圍繞BeanDefinitionReaderUtils#registerBeanDefinition
展開(kāi)分析下Bean注冊(cè)過(guò)程
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); } } }
上面無(wú)論是注冊(cè)bd還是建立alias-beanName之間的關(guān)系,均用到了BeanDefinitionRegistry
,因此我們就以它為突破口來(lái)展開(kāi)
對(duì)圖中常用接口或類進(jìn)行說(shuō)明:
ListableBeanFactory
集合類型BeanFactory 提供一種可以查找所有Bean實(shí)例的能力
getBeanNamesForType(Class)
根據(jù)類型去查找Bean名稱列表不會(huì)強(qiáng)制Bean的初始化,可從源碼中看出來(lái)
getBeansOfType(Class)
根據(jù)類型去查找Bean實(shí)例列表,會(huì)強(qiáng)制Bean的初始化,可從源碼中看出來(lái)
getBeanNamesForAnnotation(Class)
根據(jù)注解類型獲取Bean名稱列表
getBeansWithAnnotation(Class)
根據(jù)注解類型獲取Bean實(shí)例列表
findAnnotationOnBean(String,Class)
根據(jù)指定名稱+標(biāo)注類型獲取Bean實(shí)例
Hierarchical([?ha???rɑ?k?kl])BeanFactory
層次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory
設(shè)置其父容器
getParentBeanFactory()
獲取父容器
boolean containsLocalBean(String name)
在當(dāng)前容器中查找是否存在該名稱的Bean實(shí)例
SingletonBeanRegistry
單實(shí)例BeanFactory
,與單實(shí)例有關(guān)
ConfigurableBeanFactory
可配置的BeanFactory
,這個(gè)一般不用于應(yīng)用程序,是給其他BeanFactory擴(kuò)展用的。的確,定義了很多配置方法
ConfigurableListableBeanFactory
可配置的集合類型的BeanFactory
AutowireCapableBeanFactory
提供具有自動(dòng)裝配能力的BeanFactory
透過(guò)繼承體系可以看出,BeanDefinitionRegistry
的實(shí)現(xiàn)類是DefaultListableBeanFactory
,該類同時(shí)實(shí)現(xiàn)了諸多接口,可謂是BeanFactory中集大成者,因此我們到DefaultListableBeanFactory
中閱讀下bd注冊(cè)及別名注冊(cè)的源碼
先來(lái)分析下DefaultListableBeanFactory
的幾個(gè)重要的成員屬性
// 這個(gè)實(shí)質(zhì)上就是IoC容器中Bean的載體,沒(méi)錯(cuò) 它很重要,但它是無(wú)序的 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); //它代表了bd名稱的集合,它是有序的 遵循bd注冊(cè)的順序 private volatile List<String> beanDefinitionNames = new ArrayList<>(256); // 這是已創(chuàng)建bd名稱的集合,在doGetBean方法根據(jù)beanName創(chuàng)建Bean時(shí),beanName會(huì)被加到此集合中 private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
上面兩個(gè)屬性都比較重要,兩者結(jié)合使用的話可以實(shí)現(xiàn)bd的順序訪問(wèn)(其實(shí)就是遍歷beanDefinitionNames集合時(shí),使用beanDefinitionMap去獲取bd)
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //對(duì)beanName、bd進(jìn)行非空驗(yàn)證 Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); //如果bd是AbstractBeanDefinition類型,則對(duì)bd進(jìn)行驗(yàn)證(一般情況下 我們場(chǎng)景的bd都是繼承自AbstractBeanDefinition的) if (beanDefinition instanceof AbstractBeanDefinition) { try { //bd驗(yàn)證 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { //省略異常代碼 } } //從beanDefinitionMap根據(jù)beanName取bd BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); //如果beanName名稱的bd已經(jīng)存在 if (existingDefinition != null) { //如果不允許Bean被重新注冊(cè) 則拋出異常,這里默認(rèn)值是true if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } //如果已被注冊(cè)bd的角色值小于當(dāng)前待注冊(cè)bd的角色值 else if (existingDefinition.getRole() < beanDefinition.getRole()) { // 省略日志輸出 } //如果已注冊(cè)的同名bd 與本次注冊(cè)的bd不相同 else if (!beanDefinition.equals(existingDefinition)) { //省略日志輸出 } else { //省略日志輸出 } //將beanName-bd鍵值對(duì)放入beanDefinitionMap集合 this.beanDefinitionMap.put(beanName, beanDefinition); } else { //流程走到這里 說(shuō)明在beanDefinitionMap中不存在同名bd //條件成立 說(shuō)明alreadyCreated不為空 即有bd已被創(chuàng)建 if (hasBeanCreationStarted()) { // 如果在此之間 有bean正在被創(chuàng)建 則這里進(jìn)行加鎖處理 synchronized (this.beanDefinitionMap) { //將beanName-bd鍵值對(duì)放入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是手動(dòng)注冊(cè)的單例Bean名稱,則更新manualSingletonNames if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); //這里從集合中刪除的原因個(gè)人理解: //manualSingletonNames記錄的是在registerSingleton時(shí)被添加的單實(shí)例beanName,而這里注入的不是單實(shí)例Bean。因?yàn)閙anualSingletonNames包含了此beanName,因此需要剔除 updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { //如果沒(méi)有bean在被創(chuàng)建 //將beanName-bd鍵值對(duì)放入beanDefinitionMap集合 this.beanDefinitionMap.put(beanName, beanDefinition); //將beanName添加到集合中 this.beanDefinitionNames.add(beanName); //這里從manualSingletonNames中剔除,個(gè)人理解為拖地操作,畢竟若集合中沒(méi)有此beanName 也remove不了 this.manualSingletonNames.remove(beanName); } //這個(gè)集合表示凍結(jié)配置時(shí)緩存的beanName集合,暫時(shí)未理解透此集合的用途 this.frozenBeanDefinitionNames = null; } //如果已存在同名bd或已存在同名的單例對(duì)象,則重置所有已被緩存的同名的bd數(shù)據(jù),因此這里bd注冊(cè)成功后,肯定后續(xù)還會(huì)再生成Bean的 if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
其實(shí)分析下來(lái)發(fā)現(xiàn)Bean注冊(cè)的過(guò)程還是比較容易理解的,下面試著總結(jié)一下:
若bd未被注冊(cè)過(guò),則將bd信息存入BeanDefinitionMap等集合中
若bd已被注冊(cè)過(guò),允許覆蓋注冊(cè)的情況下,將bd信息存入BeanDefinitionMap等集合中,并清除已被緩存的同名bd信息
下面看一下清除bd信息的代碼邏輯
protected void resetBeanDefinition(String beanName) { // 如果此bd屬于被合并的BeanDefinition,則這里將其從MergeBeanDefinition集合中剔除 clearMergedBeanDefinition(beanName); // 如果已存在同名的單例對(duì)象 則銷毀,具體細(xì)節(jié)先不展開(kāi) destroySingleton(beanName); // 這里for循環(huán)邏輯與MergeBeanDefinition相關(guān),如果存在MergedBeanDefinitionPostProcessor,則重置此bd for (BeanPostProcessor processor : getBeanPostProcessors()) { if (processor instanceof MergedBeanDefinitionPostProcessor) { ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName); } } // BeanDefinition運(yùn)行有層級(jí)的,如果此bd擁有多個(gè)父級(jí)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的注冊(cè),再來(lái)看別名的注冊(cè) 發(fā)現(xiàn)流程比較清晰,基本上一目了然。
//注意 這里的name 不要具象為beanName,雖然我們是從建立beanName--alias關(guān)系出發(fā)追溯到這里的 public void registerAlias(String name, String alias) { //對(duì)name、alias進(jìn)行斷言驗(yàn)證 Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { //如果別名與beanName相同,那別名就沒(méi)有必要存在了,因此選擇直接從this.aliasMap中移除此別名 if (alias.equals(name)) { this.aliasMap.remove(alias); //省略日志輸出 } else { //從aliasMap中根據(jù)別名獲取name String registeredName = this.aliasMap.get(alias); if (registeredName != null) { //如果已存在的registeredName與此此要注冊(cè)的name一致,那就沒(méi)必要注冊(cè)了 if (registeredName.equals(name)) { return; } //流程走到這里,說(shuō)明同一個(gè)別名,對(duì)應(yīng)兩個(gè)name,如果不允許alias覆蓋 則拋出異常 if (!allowAliasOverriding()) { //省略異常及日志輸出 } //這里對(duì)alias進(jìn)行循環(huán)檢查,避免出現(xiàn)A的別名是B,B的別名是A的情況 checkForAliasCircle(name, alias); //將alias--name 放入aliasMap this.aliasMap.put(alias, name); //省略日志輸出 } } }
alias與beanName的映射關(guān)系,為根據(jù)名稱查找Bean又提供了一種思路。就是說(shuō)除了根據(jù)beanName外,也可以根據(jù)alias去查找Bean。
這部分源碼如下
//name可以是beanName,也可以是alias public String canonicalName(String name) { //局部變量賦值 String canonicalName = name; // Handle aliasing... String resolvedName; do { //如果從aliasMap中能根據(jù)alias分析出beanName resolvedName = this.aliasMap.get(canonicalName); if (resolvedName != null) { canonicalName = resolvedName; } } while (resolvedName != null); // 無(wú)論入?yún)ame是beanName還是alias,這里返回的都應(yīng)該是beanName了 return canonicalName; }
到此,關(guān)于“Spring Bean中Bean的注冊(cè)是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。