Spring IOC 容器源码分析 - getBean调用方法解析

1.简介

  上一节,走马观花过了一遍getBean的流程,本章会深入分析主流程中调用到的各种方法,深入解析细节。


2.transformedBeanName方法

  该方法,主要的作用是对参数中的name进行转义,为什么不能直接用name呢? 我们一起来看看

    protected String transformedBeanName(String name) {
        // 这里调用了两个方法:BeanFactoryUtils.transformedBeanName(name) 和 canonicalName
        return canonicalName(BeanFactoryUtils.transformedBeanName(name));
    }

  transformedBeanName(String name),该方法用于处理&字符,其中&字符的意义是:区分bean和FactoryBean。

    public static String transformedBeanName(String name) {
        Assert.notNull(name, "'name' must not be null");
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        // 循环处理 & 字符。比如 name = "&&&&&helloService",最终会被转成 helloService
        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
            do {
                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
            }
            while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
            return beanName;
        });
    }

  canonicalName(String name),该方法的作用是将别名解析成真正的beanName,应该spring缓存中是不用别名来作为bean的key的。

    public String canonicalName(String name) {
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        /*
         * 这里使用 while 循环进行处理,原因是:可能会存在多重别名的问题,即别名指向别名。比如下面
         * 的配置:
         *   <bean id="hello" class="service.Hello"/>
         *   <alias name="hello" alias="aliasA"/>
         *   <alias name="aliasA" alias="aliasB"/>
         *
         * 上面的别名指向关系为 aliasB -> aliasA -> hello,对于上面的别名配置,aliasMap 中数据
         * 视图为:aliasMap = [<aliasB, aliasA>, <aliasA, hello>]。通过下面的循环解析别名
         * aliasB 最终指向的 beanName
         */
        do {
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        return canonicalName;
    }

2.getSingleton(String beanName)方法


  name解析完成以后,首先会通过这个方法尝试从缓存中找bean。

    /**
     * 对于单例 bean,Spring 容器只会实例化一次。后续再次获取时,只需直接从缓存里获取即可,无需且不能再次实例化(否则单例就没意义了)。
     */
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

  getSingleton(String beanName, boolean allowEarlyReference) 该方法涉及了引用的循环依赖的处理,首先需要明确spring循环依赖的处理。

2.1.循环依赖的产生和解决的前提

  • A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象
  • A的构造方法中依赖了B的实例对象,同时B的某个field或者setter需要A的实例对象,以及反之
  • A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象,以及反之

  当然,Spring对于循环依赖的解决不是无条件的,首先前提条件是针对scope单例并且没有显式指明不需要解决循环依赖的对象,而且要求该对象没有被代理过。同时Spring解决循环依赖也不是万能,

以上三种情况只能解决两种,第一种在构造方法中相互依赖的情况Spring也无力回天。结论先给在这,下面来看看Spring的解决方法,知道了解决方案就能明白为啥第一种情况无法解决了。

2.2.Spring对于循环依赖的解决

  Spring循环依赖的理论依据其实是Java基于引用传递,当我们获取到对象的引用时,对象的field或者或属性是可以延后设置的。
Spring单例对象的初始化其实可以分为三步:
  • createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate
  • populateBean,填充属性,这步对spring xml中指定的property进行populate
  • initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法
    会发生循环依赖的步骤集中在第一步和第二步。

2.3.三级缓存

  对于单例对象来说,在Spring的整个容器的生命周期内,有且只存在一个对象,很容易想到这个对象应该存在Cache中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”。

扫描二维码关注公众号,回复: 10755305 查看本文章
缓存
用途
singletonObjects
用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects
用于存放还在初始化中的 bean,用于解决循环依赖
singletonFactories
用于存放 bean 工厂。bean 工厂所产生的 bean 是还未完成初始化的 bean。如代码所示,bean
工厂所生成的对象最终会被缓存到 earlySingletonObjects 中



 
 
 
 
 
 
 
 
 

猜你喜欢

转载自www.cnblogs.com/w2116o2115/p/12691252.html