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单例对象的初始化其实可以分为三步:
Spring单例对象的初始化其实可以分为三步:
- createBeanInstance, 实例化,实际上就是调用对应的构造方法构造对象,此时只是调用了构造方法,spring xml中指定的property并没有进行populate
- populateBean,填充属性,这步对spring xml中指定的property进行populate
- initializeBean,调用spring xml中指定的init方法,或者AfterPropertiesSet方法
会发生循环依赖的步骤集中在第一步和第二步。
2.3.三级缓存
对于单例对象来说,在Spring的整个容器的生命周期内,有且只存在一个对象,很容易想到这个对象应该存在Cache中,Spring大量运用了Cache的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”。
缓存 |
用途 |
singletonObjects |
用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用 |
earlySingletonObjects |
用于存放还在初始化中的 bean,用于解决循环依赖 |
singletonFactories |
用于存放 bean 工厂。bean 工厂所产生的 bean 是还未完成初始化的 bean。如代码所示,bean 工厂所生成的对象最终会被缓存到 earlySingletonObjects 中 |