<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
寫在前面
注:本文章使用的 SpringBoot 版本為 2.2.4.RELEASE,其 Spring 版本為 5.2.3.RELEASE
書接上文,BeanDefinition註冊到IoC容器後,緊接著就是要使用Bean了,要使用必須先要獲取Bean,這裡我們就以DefaultListableBeanFactory#getBean
方法來引出本次討論的內容:BeanDefinition的合併
通過前面的章節我們瞭解到了BeanDefinition,那什麼是BeanDefinition的合併呢?為什麼要進行合併呢? 帶著這個問題,我們到原始碼中去找找答案。
為了使原始碼邏輯有個參照,這裡先給出一個案例,在分析原始碼時 將這個案例也代入進去方便我們理解原始碼
實體類
@Data public class SuperUser implements Serializable { private String address; public SuperUser(String address) { this.address = address; } public SuperUser() { } } @Data @ToString(callSuper = true) public class User extends SuperUser { private String name; private Integer age; public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } }
基於GenericBeanDefinition註冊有層次的Bean
public class GenericBeanDefinitionDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //父BeanDefinition GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition(); rootBeanDefinition.setBeanClass(SuperUser.class); //設定引數 MutablePropertyValues propertyValues = new MutablePropertyValues(); propertyValues.addPropertyValue("address", "地址"); rootBeanDefinition.setPropertyValues(propertyValues); //子BeanDefinition GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition(); childBeanDefinition.setBeanClass(User.class); //設定構造引數 ConstructorArgumentValues argumentValues = new ConstructorArgumentValues(); argumentValues.addIndexedArgumentValue(0, "我就是我"); argumentValues.addIndexedArgumentValue(1, 18); childBeanDefinition.setConstructorArgumentValues(argumentValues); childBeanDefinition.setParentName("superUser"); //型別相同時 以子類為主 childBeanDefinition.setPrimary(true); context.registerBeanDefinition("superUser", rootBeanDefinition); context.registerBeanDefinition("user", childBeanDefinition); context.refresh(); User user = context.getBean("user", User.class); System.out.println(user); SuperUser superUser = context.getBean("superUser", SuperUser.class); System.out.println(superUser); context.close(); } }
在分析原始碼時我們要有側重點,這裡會將不太相關的邏輯一帶而過。
AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //將name解析為beanName,如果傳入的是alias,根據aliasMap進行轉換,我們在前面介紹過了 final String beanName = transformedBeanName(name); Object bean; // 如果是單例Bean Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { //省略紀錄檔輸出 // 這裡的邏輯是根據beanName判斷是否為FactoryBea,並採用相應方式去處理 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } //如果不是單例物件 else { // 對原型物件進行驗證,如果當前beanName已經在建立中了 丟擲異常 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 獲取父BeanFactory,前面我們介紹過了 BeanFactory允許有層級,可已存在父BeanFactory BeanFactory parentBeanFactory = getParentBeanFactory(); //如果存在父BeanFactory 去父BeanFactory中查詢bean if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { //省略去父BeanFactory查詢Bean的過程 } //typeCheckOnly預設為false ,這裡將beanName放到alreadyCreated集合中 表示該Bean正在建立中 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { // 這裡來到了我們要重點關注的地方了,bd的合併 ⭐️ final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); //如果存在依賴Bean,需要進行依賴查詢 String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { // 省略dependsOn 依賴查詢程式碼 } // 這裡的if..else if .. else 是根據scope取值來的 //scope=singleton時 if (mbd.isSingleton()) { //省略單範例Bean建立過程 } //scope=prototype時 else if (mbd.isPrototype()) { //省略Prototype Bean建立過程 } //scope=request、application、session時 else { // 省略其他Scope Bean的建立過程 } if (requiredType != null && !requiredType.isInstance(bean)) { //省略型別轉換程式碼 } // 返回建立的Bean return (T) bean; }
上面的方法實現比較長、比較複雜,這裡只對重要的地方進行些註釋說明並將與本次討論無關的程式碼先行進行註釋。
下面就進入到BeanDefinition的合併邏輯了
//假設beanName=user protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // 檢查快取,若存在直接返回 RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null) { return mbd; } //getBeanDefinition(beanName)==>實際上去DefaultListableBeanFactory.beanDefinitionMap中根據key查詢BeanDefinition,這在註冊階段已經放到beanDefinitionMap了。 return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); }
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { return getMergedBeanDefinition(beanName, bd, null); } //根據上面的舉例可知beanName=user,bd是User類的BeanDefinition,containingBd=null protected RootBeanDefinition getMergedBeanDefinition( String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; // 嘗試從快取中拿 if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } if (mbd == null) { //如果當前BeanDefinition沒有指定parentName,說明其不存在父BeanDefinition,不需要合併。以RootBeanDefinition形式展現 if (bd.getParentName() == null) { // 如果bd是RootBeanDefinition型別,直接型別轉換 if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { //通過bd屬性構造RootBeanDefinition mbd = new RootBeanDefinition(bd); } } else { // 走到這裡說明存在parentName,當前bd需要與其父bd合併 BeanDefinition pbd; try { //得到父BeanName String parentBeanName = transformedBeanName(bd.getParentName()); //!beanName.equals(parentBeanName) 條件成立 說明當前beanName屬於子bd if (!beanName.equals(parentBeanName)) { //遞迴地以父bd名稱 查詢父BeanDefinition。之所以遞迴地查詢,是因為 可能此時的parentBeanName還有父,實體類存在多重繼承關係 pbd = getMergedBeanDefinition(parentBeanName); } else { //走到這裡,說明beanName.equals(parentBeanName),很有可能是父bd查詢BeanDefinition時走來的。 //獲取父BeanFactory,BeanFactory也是有層次的,有父子關係的,可參見ConfigurableBeanFactory#setParentBeanFactory BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // pbd是父BeanDefinition,由其構造為RootBeanDefinition mbd = new RootBeanDefinition(pbd); //bd是子BeanDefinition,主要是繼承父類別的屬性,並覆蓋與父類別同名的屬性,有興趣的可以看一下overrideFrom方法實現 mbd.overrideFrom(bd); } // 如果父bd未指定scope,則設定預設值 if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON); } //由於containingBd=null 這裡就不看了 if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } //最終返回根據當前beanName找到的bd return mbd; } }
分析了上面的原始碼,我們試著總結一下:
1、如果不存在parentName,即不需要被合併,直接將bd轉為RootBeanDefinition 返回即可
2、如果存在parentName
本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注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