Spring读书笔记——bean创建(下)

有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充。

从缓存中加载单例

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

在看这段代码之前,我们先了解下Spring对于单例bean出现循环依赖的解决方法。如果出现上面我们介绍的A->B->C->A的情况,那是不是说Spring就无能为力了,显然Spring没有那么弱。那么Spring是怎么做的?
鉴于单例bean的循环依赖问题,Spring创建bean的原则是不等bean创建完成就会将bean的ObjectFactory提前曝光加入到缓存中,一旦有某个bean创建时需要依赖这个bean了,那么就可以直接使用ObjectFactory。
简单说,创建bean的时候,就是打包快递发货,主管为了知道你今天要派发多少个包裹,为了节省大家时间以及以免统计漏掉的情况。你可以先拿出一个包裹箱子,上面写上要寄收件人、收货地址、联系方式等等,但是这时候还没有往里面打包真正的快递。
这里曝光的bean就相当于这个快递箱子。

好了,知道了这个原则之后,我们就好理解代码了。
首先从singletonObjects中获取实例,取不到则从earlySingletonObjects中获取,仍然取不到,我们还可以到singletonFactories中获取相应的ObjectFactory,在调用这个ObjectFactory的getObject方法来创建bean。
然后将其加入到earlySingletonObjects中,在将其从singletonFactories中删除。

想必,你已经被这些用来存储和删除的集合搞疯了,没关系,我们来理一下:

  • singletonObjects
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

用于保存BeanName和创建bean实例之间的关系。

  • singletonFactories:
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);

用于保存BeanName和创建bean的工厂之间的关系

  • earlySingletonObjects:
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

用于保存BeanName和创建bean的工厂之间的关系,与singletonObjects的区别是当一个bean被放入这个集合后,可以用于其他bean做循环依赖检查

bean实例化

我们从缓存中拿到bean之后,就需要根据bean的不同类型做不同的处理,返回相应的bean,实现这个功能的就是getObjectForBeanInstance方法

protected Object getObjectForBeanInstance(
 Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

 // Don't let calling code try to dereference the factory if the bean isn't a factory.
 if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
 throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
 }

 // Now we have the bean instance, which may be a normal bean or a FactoryBean.
 // If it's a FactoryBean, we use it to create a bean instance, unless the
 // caller actually wants a reference to the factory.
 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
 return beanInstance;
 }

 Object object = null;
 if (mbd == null) {
 object = getCachedObjectForFactoryBean(beanName);
 }
 if (object == null) {
 // Return bean instance from factory.
 FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
 // Caches object obtained from FactoryBean if it is a singleton.
 if (mbd == null && containsBeanDefinition(beanName)) {
 mbd = getMergedLocalBeanDefinition(beanName);
 }
 boolean synthetic = (mbd != null && mbd.isSynthetic());
 object = getObjectFromFactoryBean(factory, beanName, !synthetic);
 }
 return object;
 }
  • 首先检测指定的beanName是否是工厂bean相关,如果既不是工厂bean又是以"&"开头,则校验失败,抛出异常
  • 如果这个bean不是工厂Bean(FactoryBean),那么就直接返回bean实例
  • 剩下代码就是处理FactoryBean,我们顺着这样的顺序依次来到getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean
private Object doGetObjectFromFactoryBean(
 final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
 throws BeanCreationException {

 Object object;
 try {
 if (System.getSecurityManager() != null) {
 AccessControlContext acc = getAccessControlContext();
 try {
 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
 public Object run() throws Exception {
 return factory.getObject();
 }
 }, acc);
 }
 catch (PrivilegedActionException pae) {
 throw pae.getException();
 }
 }
 else {
 object = factory.getObject();
 }
 }
 catch (FactoryBeanNotInitializedException ex) {
 throw new BeanCurrentlyInCreationException(beanName, ex.toString());
 }
 catch (Throwable ex) {
 throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
 }

 // Do not accept a null value for a FactoryBean that's not fully
 // initialized yet: Many FactoryBeans just return null then.
 if (object == null && isSingletonCurrentlyInCreation(beanName)) {
 throw new BeanCurrentlyInCreationException(
 beanName, "FactoryBean which is currently in creation returned null from getObject");
 }

 if (object != null && shouldPostProcess) {
 try {
 object = postProcessObjectFromFactoryBean(object, beanName);
 }
 catch (Throwable ex) {
 throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
 }
 }

 return object;
 }

这么长的代码,如果嫌累,就只看factory.getObject()这行就好,这诠释了FactoryBean的加载时通过factory.getObject的方式获取到对应的bean实例的。

如何创建单例bean

在上篇的doGetBean方法中,如果从缓存中加载不到,那么我们就需要老老实实的从头开始加载bean了,对于单例bean的加载就都在这里实现了

// Create bean instance.
 if (mbd.isSingleton()) {
 sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
 public Object getObject() throws BeansException {
 try {
 return createBean(beanName, mbd, args);
 }
 catch (BeansException ex) {
 // Explicitly remove instance from singleton cache: It might have been put there
 // eagerly by the creation process, to allow for circular reference resolution.
 // Also remove any beans that received a temporary reference to the bean.
 destroySingleton(beanName);
 throw ex;
 }
 }
 });
 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
 }

创建bean

我们从AbstractBeanFactory的createBean方法来到了AbstractAutowiredCapableBeanFactory的createbean方法,而真正的创建bean其实在doCreateBean方法中

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {// Instantiate the bean.   BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);// Allow post-processors to modify the merged bean definition.   synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references   // even when triggered by lifecycle interfaces like BeanFactoryAware.   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}addSingletonFactory(beanName, new ObjectFactory() {public Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// Initialize the bean instance.   Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {exposedObject = initializeBean(beanName, exposedObject, mbd);}}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {Object earlySingletonReference = getSingleton(beanName, false);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.   try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
  • 如果是单例Bean,那么首先是从factoryBeanInstanceCache中清除该beanName对应的记录
  • 实例化bean,将BeanDefinition转换为BeanWrapper对象
  • bean合并后的处理
  • 解决循环依赖问题
  • 属性填充,将所有属性填充到bean的实例中
    这个方法,本身不算长,但是层层深入就会发现其下面包罗了创建bean的诸多繁杂的细节(这块自己看的也是云里雾里,就不往下延伸扩展了)。

虽然对于Spring加载bean,我只写了四篇,但是其内部实现远比我表述的要复杂的多。
看源码确实很煎熬,对于目前看不懂的地方要么多看几遍,要么先跳过。阅读代码的过程中要懂得取舍,对于非重点部分比如日志或者异常处理可以先忽略,沿着一条主线往下看,最主要是先弄懂代码的只要意图。
Spring的bean加载代码量虽然巨大,但是思路还是比较清晰的,我们知道Spring如何加载xml然后解析xml,再到如何把xml的元素转为自己的BeanDefinition,最后又是如何取出对应的beanName然后返回一个bean实例供容器使用的。

网上有一位大神用一张图就把整个过程画出来了

image.png

注:图片来源http://blog.csdn.net/zghwaicsdn/article/details/50910384

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

推荐阅读更多精彩内容