spring容器之不同scope bean的创建

关于不同Scope的bean,spring通过不同的策略来处理以及创建过程,在整个过程中我们知道默认为Singleton,当然还有prototype和request等等,首先我们来看spring对Singleton的处理过程:

 AbstractBeanFactory.java
//单例的情况下
if (mbd.isSingleton()) {
//从缓存(singletonObjects)中去拿
sharedInstance = getSingleton(beanName, () -> {
    try {
        return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
            //最后从缓存中移除对应的实例原因有:
            //1.每当有bean在创建时,允许循环参考来解析对应的beanDefinition
            //2.删除临时引用该bean的bean
              destroySingleton(beanName);
                throw ex;
            }
        });

    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

方法很明确,当我们合并后rootBeanDefinition是单例的时候,通过调用#getSingleton(String beanName, ObjectFactory<?> singletonFactory)来实现,关于spring获取Singleton bean的过程请参考spring容器之单例bean的获取这篇文章,若如果在缓存中没有了spring又是如何处理的呢?接着看:

  DefaultSingletonBeanRegistry.java
/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    //考虑到并发情况,锁住全局变量
    synchronized (this.singletonObjects) {
        //1.通过参数beanName从singletonObjects缓存中获取
        Object singletonObject = this.singletonObjects.get(beanName);
        //如果没获取到
        if (singletonObject == null) {
            //1.1. 判断当前bean是否是销毁状态
            if (this.singletonsCurrentlyInDestruction) {
                throw new BeanCreationNotAllowedException(beanName,
                        "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                        "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
            }
            //2.单例bean创建之前的检验
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            //2.1.异常集合的初始化过程
            //suppressedExceptions用来保存单例bean获取过程中的不必要的异常
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            try {
                //3.初始化singletonObject实例
                //该过程实际上调用的createBean()方法来做处理
                singletonObject = singletonFactory.getObject();
                newSingleton = true;
            }
            catch (IllegalStateException ex) {
                // Has the singleton object implicitly appeared in the meantime ->
                // if yes, proceed with it since the exception indicates that state.
                singletonObject = this.singletonObjects.get(beanName);
                if (singletonObject == null) {
                    throw ex;
                }
            }
            catch (BeanCreationException ex) {
                if (recordSuppressedExceptions) {
                    for (Exception suppressedException : this.suppressedExceptions) {
                        ex.addRelatedCause(suppressedException);
                    }
                }
                throw ex;
            }
            finally {
                if (recordSuppressedExceptions) {
                    this.suppressedExceptions = null;
                }
                //单例bean创建之后的处理过程
                afterSingletonCreation(beanName);
            }
            //将创建之后的bean保存到singletonObjects中
            if (newSingleton) {
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}

这就是当缓存中没有时,spring的处理过程,还是尝试着去从缓存中获取,如果没有获取到,通过ObjectFactory#getObject()进行bean实例的初始化,然后在保存到singletonObjects缓存中,在整个的过程中,实际上我们发现并没有看到创建bean的过程,只是做了一些创建bean前提的准备工作,我们来看一下都做了些什么:

  • 首先在1处尝试着去获取,如果有的话直接返回该bean的instance即可
  • 如果在缓存没获取到,开始进行bean的加载过程
  • 在2处我们可以看到的是首先在加载bean之前的检验,在之前的文章单例bean的获取过程说过了,这里就不多说了.
  • 检验之后首先是对异常集合的初始化过程
private Set<Exception> suppressedExceptions;
//2.1.异常集合的初始化过程
  //suppressedExceptions用来保存单例bean获取过程中的不必要的异常
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
      this.suppressedExceptions = new LinkedHashSet<>();
    }
  • 前提的过程完成后,我们可以看到在3处是真正的初始化bean的过程,可以看到的是通过我们的参数ObjectFactory#getObject()来完成,如何实现我们后续说.
  • 在我们bean实例初始化完成后,进行后续的扫尾工作,对我们实例化的bean进行不能重复创建的标记处理,下次获取直接获取即可,关于afterSingletonCreation(String beanName)详细讲解我们在单例bean获取的过程中详细的说过了,这里不再重复了.
  • 最后将我们实例化的bean保存在singletonObjects缓存中
/**存放单例bean的映射集合map*/
//对应关系: bean  name -----> bean  instance
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**存放objectFactory的map映射集合*/
//对应关系: bean  name-----------> objectFactory
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**存放早期单例bean的映射集合*/
//对应关系: bean   name ---------> bean instance
//与singletonObjects不同区别是,如果单例的bean保存到earlySingletonObjects时,如果该bean此时处于创建过程中,可以直接通过getBean()方法获取,也就是提早的暴露该bean,主要是为了解决循环依赖的问题
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**存放的是当前已经完成注册的bean*/
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/**
 * 将单例的bean保存到缓存中
 * @param beanName
 * @param singletonObject
 */
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        //保存到singletonObjects缓存里
        this.singletonObjects.put(beanName, singletonObject);
        //从singletonFactories工厂缓存中移除相应的bean的实例
        this.singletonFactories.remove(beanName);
        //从早期单例缓存中移除对应的bean实例
        this.earlySingletonObjects.remove(beanName);
        //将该bean添加注册表中
        this.registeredSingletons.add(beanName);
    }
}

以上就是缓存中不存在的情况下,spring还是通过#getSingleton(String beanName, ObjectFactory<?> singletonFactory)来完成了单例bean的获取

  • 完成了单例bean的获取之后,紧接着是通过#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) 方法来获取我们想要的bean对象,关于该方法的详细过程在之前的[spring容器之从bean的实例中获取对象spring容器之从bean的实例中获取对象
    )详细的讲解了,感兴趣的可以去看看
    #######原型(prototype)模式
AbstractBeanFactory.java
//原型的情况下:
else if (mbd.isPrototype()) {

      Object prototypeInstance = null;
    try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
                    //创建完bean的处理
                  afterPrototypeCreation(beanName);
        }
        bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

上面代码就是spring对原型模式的处理过程,很简单,直接调用#createBean()方法创建一个bean即可

  1. 先是判断是否是原型(prototype)
  2. 调用#beforePrototypeCreation(String beanName)方法进行原型创建之前的状态记录
/** Names of beans that are currently in creation. */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
        new NamedThreadLocal<>("Prototype beans currently in creation");

/**
 * Callback before prototype creation.
 * <p>The default implementation register the prototype as currently in creation.
 * @param beanName the name of the prototype about to be created
 * @see #isPrototypeCurrentlyInCreation
 */
@SuppressWarnings("unchecked")
protected void beforePrototypeCreation(String beanName) {
    //从prototypesCurrentlyInCreation获取当前所有的正在创建的bean
    Object curVal = this.prototypesCurrentlyInCreation.get();
    //没有正在创建的bean,  然后进行保存
    if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
    }
    //如果是正在创建的bean是String类型的
    else if (curVal instanceof String) {
        //进行保存操作
        Set<String> beanNameSet = new HashSet<>(2);
        beanNameSet.add((String) curVal);
        beanNameSet.add(beanName);
        this.prototypesCurrentlyInCreation.set(beanNameSet);
    }
    //是set集合的的话
    else {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.add(beanName);
    }
}
  1. 状态记录完成之后,接着是调用#createBean(String beanName, RootBeanDefinition mbd, Object[] args)进行bean的创建,关于详细的创建过程后面在学习
    4.bean的创建完成后调用#afterPrototypeCreation(String beanName)进行后置处理操作.
/**
 * Callback after prototype creation.
 * <p>The default implementation marks the prototype as not in creation anymore.
 * @param beanName the name of the prototype that has been created
 * @see #isPrototypeCurrentlyInCreation
 */
@SuppressWarnings("unchecked")
protected void afterPrototypeCreation(String beanName) {
    Object curVal = this.prototypesCurrentlyInCreation.get();
    if (curVal instanceof String) {
        this.prototypesCurrentlyInCreation.remove();
    }
    else if (curVal instanceof Set) {
        Set<String> beanNameSet = (Set<String>) curVal;
        beanNameSet.remove(beanName);
        if (beanNameSet.isEmpty()) {
            this.prototypesCurrentlyInCreation.remove();
        }
    }
}

4.紧接着是通过#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) 方法来获取我们想要的bean对象,关于该方法的详细过程在之前的[spring容器之从bean的实例中获取对象spring容器之从bean的实例中获取对象
)详细的讲解了,感兴趣的可以去看看

其它scope
else {
    //获取bean的作用域
    String scopeName = mbd.getScope();
    final Scope scope = this.scopes.get(scopeName);
          if (scope == null) {
                throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }
                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        beforePrototypeCreation(beanName);
                        try {
                            return createBean(beanName, mbd, args);
                        }
                        finally {
                            afterPrototypeCreation(beanName);
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }

处理的流程都差不多,只是在获取bean的过程不一样,是通过Scope#get(String name, ObjectFactory<?> objectFactory)方法来实现,代码如下:

SimpleThreadScope.java
private final ThreadLocal<Map<String, Object>> threadScope =
        new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
            @Override
            protected Map<String, Object> initialValue() {
                return new HashMap<>();
            }
        };

public Object get(String name, ObjectFactory<?> objectFactory) {
    //获取scope
    Map<String, Object> scope = this.threadScope.get();
    //获取bean实例
    Object scopedObject = scope.get(name);
    //如果为null的话
    if (scopedObject == null) {
        //调用ObjectFactory的getobject方法获取实例
        scopedObject = objectFactory.getObject();
        //保存操作
        scope.put(name, scopedObject);
    }
    return scopedObject;
}

该方法位于org.springframework.beans.factory.config.Scope 接口,在该接口中还有很多方法,感兴趣的可以自己去看看

总结

本篇文章主要是针对于不同作用域的bean进行处理的过程,其实质上都很类似,上面说的也很清楚了,这里就不啰嗦了

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容