zoukankan      html  css  js  c++  java
  • Spring IoC源码解读——谈谈bean的几种状态

    阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解。这里从bean的几个状态的角度出发,研究下IoC容器。

    一、原材料

      Xml中的bean定义配置(或者注解)、及Java代码

        <bean id="book" name="book" class="com.sky.vo.Book" scope="singleton" init-method="productBook" destroy-method="destroyBook">
            <property name="title" value="小王子"/>
        </bean>
    public class Book implements InitializingBean, DisposableBean, BeanFactoryAware, ApplicationContextAware{
        private String title;
        
        public Book(){
            System.out.println("调用了Book默认构造函数");
        }
        
        public void productBook(){
            System.out.println("书本初始化init-method");
        }
      ///.......
    }

    二、半成品

      BeanDefinition是Spring中很重要的一个接口,作用是对bean的一种抽象(简单的理解是他的属性对应了<bean>中定义的属性和子节点等信息)。他是半成品,所以我们几乎不会直接使用,而是在IoC容器内部流通!

    阅读源码发现是用SAX解析xml,然后保存到BeanDefinition对象。以下是关键的伪代码

    /*
    **BeanDefinitionParserDelegate.java
    */
    public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
        AbstractBeanDefinition bd = createBeanDefinition(className, parent){
          GenericBeanDefinition bd = new GenericBeanDefinition();
          bd.setBeanClassName(className);
          return bd;       
      }
    
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        parseConstructorArgElements(ele, bd);
        parsePropertyElements(ele, bd);
      //省略了解析其他子节点的代码
    }

    核心的解析过程比较简单,创建了GenericBeanDefinition,然后节点属性和<property>、<construct-arg>等设置为他的属性。

    那么,还有一个比较关键的步奏,就是转化后的BeanDefinition存放的时机和位置,是在Spring IoC容器启动时存放的,具体代码:

    /*
    **DefaultListableBeanFactory.java
    */
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
            synchronized (this.beanDefinitionMap) {
                Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
                if (oldBeanDefinition != null) {
                    if (!this.allowBeanDefinitionOverriding) {
                        throw new BeanDefinitionStoreException("Cannot register bean definition  for bean , There is already bound.");
                    }
                    else {
                        this.logger.info("Overriding bean definition for bean, replacing oldBeanDefinition");
                    }
                }
                else {
                    this.beanDefinitionNames.add(beanName);
                }
                this.beanDefinitionMap.put(beanName, beanDefinition);
            }
    }

    可以看到最终存储到了beanDefinitionMap这个Map中,来看看他的定义(DefaultListableBeanFactory.java中)

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

    这样,Spring容器就持有了BeanDefinition。

    三、成品

    获取成品就是获取具体的bean对象,其实就是Object bean = BeanFactroy.getBean(beanName).这里根据scope的不同,会有一些不同,简而言之:

    A、singleton在IoC容器初始化时实例化,并缓存到Map中;getBean(singletonBeanName)时从这个Map中取即可;

      如果singleton同时lazy-init="true",第一次getBean时缓存到Map,以后从Map中取;

    B、getBean(prototypeBeanName)时每次都实例化新的bean对象;

    1、singleton的bean

      直接看伪代码(截取了部分关键代码)

        //从getBean入手
        BeanFactroy.getBean("beanName"){
            AbstractBeanFactory.createBean{
                Object beanInstance = doCreateBean(beanName, mbd, args){
                    // Eagerly check singleton cache for manually registered singletons.
                    //这是返回了缓存的singleton的bean:com.sky.vo.Book@1a1ecd
                    Object sharedInstance = getSingleton(beanName){
                        return Object singletonObject = this.singletonObjects.get(beanName);
                    }
                    return (T)bean;
                }    
            }
        }

    从源码我们可以看到,对于singleton的bean,我们直接从singletonObjects这个Map中取!

    public class DefaultSingletonBeanRegistry{
      /**缓存了singleton的bean:beanName--->bean实例*/
      private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
    }

    那么bean实例是什么时候存入到这个map的呢?看源码知道是在IoC容器启动时。

    通过多次分析源码,我们知道AbstractApplicationContext.refresh()完成了容器的初始化过程,singleton的bean的实例化也是在这完成的。

        public void AbstractApplicationContext.refresh(){
            //创建Spring容器
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            
            // Instantiate all remaining (non-lazy-init) singletons.
            AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory){
                DefaultListableBeanFactory.preInstantiateSingletons(){
                    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
                    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                        //然后,执行了getBean的操作!!!这里执行的代码就是BeanFactory.getBean(beanName)部分
                        AbstractBeanFactory.getBean(beanName){
                            AbstractBeanFactory.doGetBean(beanName){
                                //缓存为空
                                Object sharedInstance = getSingleton(beanName);
                                //如果是singleton
                                if (mbd.isSingleton()) {
                                    DefaultSingletonBeanRegistry.getSingleton(){
                                        synchronized (this.singletonObjects) {
                                            //这里缓存也是空的
                                            Object singletonObject = this.singletonObjects.get(beanName);
                                            //实例化bean
                                            singletonObject = singletonFactory.getObject(){
                                                Object beanInstance = doCreateBean(beanName, mbd, args);
                                            }
                                            //这里非常重要,把实例化的bean放到map中
                                            addSingleton(beanName, singletonObject){
                                                this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

    截取了部分关键的代码片段,可以看到Spring容器初始化时会把实例化的singleton的bean放到singletonObject

  • 相关阅读:
    "密码最短长度为7,其中必须包含以下非字母数字字符1"解决方法 (转)
    关于数据绑定的一个小小的总结:绑定数据到List类型的控件(RadioButtonList,ListBox等),双重绑定。
    转:从玩具到游戏 看另类项目激励机制
    [系列文章]上传文件管理控件之v1
    [系列文章]上传文件管理控件v3
    解决“此版本的 SQL Server 不支持用户实例登录标志。该连接将关闭”问题,完整综合版。
    Crystal Reports基本语法
    Crystal Reports图表(上)
    Crystal Reports第一张报表
    Crystal Reports中的字段
  • 原文地址:https://www.cnblogs.com/postnull/p/4980020.html
Copyright © 2011-2022 走看看