Fork me on GitHub

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的手段,在循环依赖问题的解决过程中甚至使用了“三级缓存”。

缓存
用途
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处于创建中)
  分析getSingleton的整个过程,Spring首先从singletonObjects(一级缓存)中尝试获取,如果获取不到并且对象在创建中,则尝试从earlySingletonObjects(二级缓存)中获取,如果还是获取不到并且允许从singletonFactories通过getObject获取,则通过singletonFactory.getObject()(三级缓存)获取。如果获取到了则
  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);
  Spring利用其创建bean另一处就在 doCreateBean方法中:
 // 添加工厂对象到 singletonFactories 缓存中
 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  此处就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来的。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。
 
    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可以改变一下提前暴露的对象;

 

 
posted @ 2020-04-13 14:37  啊慌  阅读(482)  评论(0)    收藏  举报