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单例对象的初始化其实可以分为三步:
- 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 中 |
2.4.解决方案
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从 singletonObjects 获取实例,singletonObjects 中缓存的实例都是完全实例化好的 bean,可以直接使用 Object singletonObject = this.singletonObjects.get(beanName); /* * 如果 singletonObject = null,表明还没创建,或者还没完全创建好。 * 这里判断 beanName 对应的 bean 是否正在创建中 */ if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 从 earlySingletonObjects 中获取提前曝光的 bean,用于处理循环引用 singletonObject = this.earlySingletonObjects.get(beanName); // 如果如果 singletonObject = null,且允许提前曝光 bean 实例,则从相应的 ObjectFactory 获取一个原始的(raw)bean(尚未填充属性) if (singletonObject == null && allowEarlyReference) { // 获取相应的工厂类 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); // 放入缓存中,如果还有其他 bean 依赖当前 bean,其他 bean 可以直接从 earlySingletonObjects 取结果 // 提前曝光 bean 实例,用于解决循环依赖 this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
- allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
- isSingletonCurrentlyInCreation 判断对应的单例对象是否在创建中,当单例对象没有被初始化完全(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中)
this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName);
则移除对应的singletonFactory,将singletonObject放入到earlySingletonObjects,其实就是将三级缓存提升到二级缓存中!
Spring解决循环依赖的诀窍就在于singletonFactories这个cache,这个cache中存的是类型为ObjectFactory,其定义如下:
public interface ObjectFactory<T> { /** * Return an instance (possibly shared or independent) * of the object managed by this factory. * @return the resulting instance * @throws BeansException in case of creation errors */ T getObject() throws BeansException; }
在bean创建过程中,有两处比较重要的匿名内部类实现了该接口。一处是
sharedInstance = getSingleton(beanName, () -> { try { // 创建 bean 实例 return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); // 如果 bean 是 FactoryBean 类型,则调用工厂方法获取真正的 bean 实例。否则直接返回 bean 实例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
// 添加工厂对象到 singletonFactories 缓存中 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { //如果一级缓存里没有 if (!this.singletonObjects.containsKey(beanName)) { //放入三级缓存 this.singletonFactories.put(beanName, singletonFactory); //从二级缓存移除 this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
//拿到经过处理的 Bean exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
在将三级缓存放入二级缓存的时候,会判断是否有SmartInstantiationAwareBeanPostProcessor这样的后置处理器,换句话说这里是给用户提供接口扩展的,所以采用了三级缓存。
所以利用SmartInstantiationAwareBeanPostProcessor可以改变一下提前暴露的对象;

浙公网安备 33010602011771号