spring源码解析八(创建单例bean)

上一节,研究了下获取bean的源码,逻辑还是比较简单的,这次,我们一起来研究下创建bean的源码,话不多说,我们直接看源码
发现bean的创建的代码都是在AbstractAutowireCapableBeanFactory这个类中,之前分析@Autowired注解的时候,也是在这个类中
createBean的其他方法我在这里就不分析了,咱们具体就分析doCreateBean

AbstractAutowireCapableBeanFactory

这里将成三个步骤,创建bean实例,填充bean的属性,执行后置处理器,
···
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {

    RootBeanDefinition mbdToUse = mbd;

    //确保当前的bean类已经被解析
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }
        mbdToUse.prepareMethodOverrides();

    try {
        //1:在获取bean之前获取一下尝试获取下代理对象
        //2:判断是不是基本类型的对象
        //3:是否需要跳过(获取增强对象,判断存不存在@Aspect注解,存在,证明是AOP类,判断切点,根据@Before等注解获取一个完整的增强对象)
        //4:将获取到的增强对象集合与缓存缓存中的advisor.getName执行比较,如果是同一个bean,直接返回
        //判断是否有自定义的targetSource,存在的话,targetSource以自定义的方式处理目标类,然后返回,否则返回null
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }

    try {
        //真正创建bean实例的方法,
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        return beanInstance;
    }

}

···
这个方法很长,咱们先简单看一下这个方法到底做了什么操作,为了看得清楚,我会删除部分抛异常代码
1:从缓存中获取bean的包装对象并删除缓存
2:缓存中获取不到bean,创建bean实例
3:解决循环依赖
4:填充bean中的属性
5:重新注册一次bean实例
···

AbstractAutowireCapableBeanFactory

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

    // 1:创建bean包装对象,
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        //如果是单例,从缓存中获取bean包装对象,并删除缓存
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        //创建bean实例,并返回bean包装对象
        //这里创建bean实例并返回bean包装对象
        //createBeanInstance方法中会通过三种方式创建bean实例
        //1:通过工厂方法创建
        //2:通过构造方法注入,比如autowire或者constructor的方式创建bean实例
        //3:通过无参构造的方式创建bean实例
        //4:如果配置文件中配置了lookup-method 和 replace-method,则会利用Cglib代理的方式增强bean实例
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }

    // 后置处理器处理流程,就不展开说了
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            mbd.postProcessed = true;
        }
    }
    //===========================================================================================
    //2.1:判断是否存在循环引用,
    //这个earlySingletonExposure的意思是:单例bean是否提前暴露
    //同时满足三个条件,才会变为true
    //1:mbd.isSingleton():是否是单例
    //2:this.allowCircularReferences:是否循环引用
    //3:isSingletonCurrentlyInCreation:该单例bean是否处于创建中
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        //添加单例工厂对象,用于处理循环引用,这个地方是利用了函数式接口,我们把它展开看,就好看一点
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

        addSingletonFactory(beanName, new ObjectFactory<Object>() {
                    @Override
                    public Object getObject() throws BeansException {
                        // 获取早期bean引用
                        return getEarlyBeanReference(beanName, mbd, bean);
                    }
                });
    }

    //=================================================================================================
    //3:填充属性
    Object exposedObject = bean;
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);

    //2.2解决循环引用
    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<>(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.");
                }
            }
        }
    }

    // 必要时再注册一次bean
    registerDisposableBeanIfNecessary(beanName, bean, mbd);

    return exposedObject;
}

···

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