【Spring源碼--IOC容器的實現】(四)BeanDefinition的註冊

  • 前言

  1. 本篇是IOC容器啓動的最後一遍,也就是註冊。但並不是DI(依賴注入)已經完成,這裏只是完成對xml文件的解析、IOC容器的啓動,具體的依賴注入需要getBean的時候完成。但是也有一個例外:那就是通過控制lazy-init屬性來讓容器實現對bean的預實例化。這個後面我們在講。
  2. 當代碼讀到這裏,可以再針對前面的代碼有目的的去回顧一下,先走通主線,再根據註釋儘量去理解一些周邊方法。
  • BeanDefinition在IOC容器註冊

    首先我們要回憶兩點:1、發起註冊的地方;2、註冊的實現類。
    我們先來看第一點,上一篇博客前半部分我們講了Bean的解析,在DefaultBeanDefinitionDocumentReader類的processBeanDefinition方法中,其中一個是去完成BeanDefinition的載入,另一個是完成註冊。我們先來看下簡略代碼:

    代碼1.1:DefaultBeanDefinitionDocumentReader類的processBeanDefinition方法

    1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    2. //解析方法
    3. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    4. if (bdHolder != null) {
    5. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    6. try {
    7. //註冊方法
    8. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    9. }
    10. catch (BeanDefinitionStoreException ex) {
    11. getReaderContext().error("Failed to register bean definition with name '" +
    12. bdHolder.getBeanName() + "'", ele, ex);
    13. }
    14. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    15. }
    16. }

    現在我們找到了BeanDefinition的註冊入口,我們知道BeanDefinitionHolder=BeanDefinition+beanName,所以這裏數據我們是有的,來看下registerBeanDefinition的代碼:

    代碼1.2:BeanDefinitionReaderUtils類的registerBeanDefinition方法

    1. public static void registerBeanDefinition(
    2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    3. throws BeanDefinitionStoreException {
    4. // 根據beanName註冊Beandefinition.
    5. String beanName = definitionHolder.getBeanName();
    6. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    7. // 如果有別名,註冊別名
    8. String[] aliases = definitionHolder.getAliases();
    9. if (aliases != null) {
    10. for (String aliase : aliases) {
    11. registry.registerAlias(beanName, aliase);
    12. }
    13. }
    14. }

    這裏我們需要回憶第二點了,因爲當我們找這個registerBeanDefinition的實現類的時候,會發現有三個實現類,那麼具體取哪個?還記得我們最初容器建立的時候用的是什麼容器麼?簡單回顧下代碼:

    代碼1.3:AbstractRefreshableApplicationContext類的refreshBeanFactory方法

    1. @Override
    2. protected final void refreshBeanFactory() throws BeansException {
    3. if (hasBeanFactory()) {
    4. destroyBeans();
    5. closeBeanFactory();
    6. }
    7. try {
    8. //創建Factory
    9. DefaultListableBeanFactory beanFactory = createBeanFactory();
    10. beanFactory.setSerializationId(getId());
    11. customizeBeanFactory(beanFactory);
    12. loadBeanDefinitions(beanFactory);
    13. synchronized (this.beanFactoryMonitor) {
    14. this.beanFactory = beanFactory;
    15. }
    16. }
    17. catch (IOException ex) {
    18. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    19. }
    20. }

    而且,我們看下這個DefaultListFactory實現了BeanDefinitionRegistry接口,這個接口就是定義註冊的。並且這個類中有這麼一個HashMap,這裏就是我們存放BeanDefinition的地方。

    1. /** Map of bean definition objects, keyed by bean name */
    2. private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    那麼我們繼續來看代碼:

    代碼1.4:DefaultListableBeanFactory類的registerBeanDefinition方法

    1. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    2. throws BeanDefinitionStoreException {
    3. Assert.hasText(beanName, "Bean name must not be empty");
    4. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    5. //校驗解析的BeanDefiniton
    6. if (beanDefinition instanceof AbstractBeanDefinition) {
    7. try {
    8. ((AbstractBeanDefinition) beanDefinition).validate();
    9. }
    10. catch (BeanDefinitionValidationException ex) {
    11. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    12. "Validation of bean definition failed", ex);
    13. }
    14. }
    15. //註冊的過程中需要線程同步,以保證數據的一致性
    16. synchronized (this.beanDefinitionMap) {
    17. Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    18. //檢查是否有同名的BeanDefinition已經在IoC容器中註冊,如果已經註冊,
    19. //並且不允許覆蓋已註冊的Bean,則拋出註冊失敗異常
    20. if (oldBeanDefinition != null) {
    21. if (!this.allowBeanDefinitionOverriding) {
    22. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    23. "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    24. "': There is already [" + oldBeanDefinition + "] bound.");
    25. }
    26. else {//如果允許覆蓋,則同名的Bean,後註冊的覆蓋先註冊的
    27. if (this.logger.isInfoEnabled()) {
    28. this.logger.info("Overriding bean definition for bean '" + beanName +
    29. "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
    30. }
    31. }
    32. }
    33. else {
    34. //IoC容器中沒有已經註冊同名的Bean,按正常註冊流程註冊
    35. this.beanDefinitionNames.add(beanName);
    36. this.frozenBeanDefinitionNames = null;
    37. }
    38. this.beanDefinitionMap.put(beanName, beanDefinition);
    39. //重置所有已經註冊過的BeanDefinition的緩存
    40. resetBeanDefinition(beanName);
    41. }
    42. }

    完了,這就結束了。到這裏,代碼就完成了IOC容器的初始化過程。此時,在使用的IOC容器DefaultListableBeanFactory中已經建立了整個Bean的配置信息,而且這些BeanDefinition已經可以被容器使用了,他們都在beanDefinitionMap中北檢索和使用。容器的作用就是對這些信息進行處理和維護。這些信息室容器建立依賴反轉的基礎,有了這些基礎數據,就可以完成最後的依賴注入了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章