前言

  • 上篇博客spring 5.0.x源码学习系列八: 实例化bean之应用构造方法创立bean、主动拆卸与循环依赖次要介绍了Spring bean的实例化过程,包含主动拆卸和依赖注入。其中有提到FactoryBean这个字眼,FactoryBean是一种非凡的bean,它能够保护两个bean并都交由spring治理。但BeanFactory它并不是一个bean,是一个一般对象,通过new关键字创立的。

    this.beanFactory = new DefaultListableBeanFactory();
  • 这里先解释下bean和一般对象的概念
  1. bean: 通过spring创立,走了spring的bean创立过程
  2. 一般对象: new进去的,未通过spring的bean创立过程

一、我的项目demo

1.1 我的项目包构造

1.2 AppConfig.java

1.3 BeanA.java

1.4 Entry.java

1.5 MyFactoryBean.java

二、运行后果

三、原理解析

3.1 要想理解FactoryBean的原理首先得先理解FactoryBean的创立过程

3.1.1 创立FactoryBean入口

3.1.2 解决bean名称和第一次getSingleton

3.1.3 创立完FactoryBean

3.2 从getObjectForBeanInstance办法中获取真正须要的bean

  • 源码及正文

    protected Object getObjectForBeanInstance(        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {    // Don't let calling code try to dereference the factory if the bean isn't a factory.    // 判断未通过解决的bean名称是否为FactoryBean的个性,    // 即是否以&符号结尾, 符合条件,然而if外部的两个条件都未满足,即可疏忽此段代码    if (BeanFactoryUtils.isFactoryDereference(name)) {        if (beanInstance instanceof NullBean) {            return beanInstance;        }        if (!(beanInstance instanceof FactoryBean)) {            throw new BeanIsNotAFactoryException(beanName, 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.    // 这个分支就是为了return一般的bean,此一般bean非一般bean    // 这里的一般bean蕴含两种含意    // 1. 类型不是FactoryBean类型    // 2. 用户的目标就是想要获取FactoryBean类型的bean    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {        return beanInstance;    }    Object object = null;    // 第二次获取context.getBean("myFactoryBean")时, 因为传入的mbd为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());        /**         * 通过了上述条件的筛选,能进入此办法肯定是         * 要获取的bean为FactoryBean外部保护的对象。         * 在外部最重要的就是调用FactoryBean的getObject办法来获取bean。         * 第一次获取是调用FactoryBean的getObject办法返回并放入缓存中factoryBeanObjectCache         */        object = getObjectFromFactoryBean(factory, beanName, !synthetic);    }    return object;}

3.2.1 从getObjectForBeanInstance办法中获取一般bean

  • 这里的一般bean蕴含两种含意
  1. 类型不是FactoryBean类型的bean
  2. 用户的目标就是想要获取FactoryBean类型的bean
  • 创立MyFactoryBean状况,属于上述第二种类型,及获取的bean类型就为FactoryBean, 所以它提前return
3.2.1.1获取MyFactoryBean状况: context.getBean("&myFactoryBean")

3.2.1.2 获取MyFactoryBean中保护的bean的状况: context.getBean("myFactoryBean")


  • doGetObjectFromFactoryBean源码

    private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)        throws BeanCreationException {    Object object;    try {        if (System.getSecurityManager() != null) {            AccessControlContext acc = getAccessControlContext();            try {                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);            }            catch (PrivilegedActionException pae) {                throw pae.getException();            }        }        else {            // 调用FactoryBean的getObject办法            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) {        if (isSingletonCurrentlyInCreation(beanName)) {            throw new BeanCurrentlyInCreationException(                    beanName, "FactoryBean which is currently in creation returned null from getObject");        }        object = new NullBean();    }    return object;}
3.2.1.3 再次获取MyFactoryBean中保护的bean的状况:: context.getBean("myFactoryBean")
  1. 在main办法中新增一行代码

四、流程总结

4.1 创立FactoryBean流程

  1. 判断以后创立bean的类型为FactoryBean, 在bean名称前增加&符号
  2. 进入doGetBean办法, 对bean名称后面的&符号进行革除,外部保护了两个bean名称,一个是name另一个是beanName。其中name为真正创立的bean名称,beanName是解决过的bean名称。最终会以beanName去创立bean,所以FactoryBean对应的bean的名称就是首字母小写
  3. 创立完bean后对立走getObjectFromFactoryBean办法。在此办法中有四个参数都比拟重要:

    beanInstance: 在doGetBean获取到的bean或者创立进去的bean对象name: 要获取真正bean的名称beanName: 解决过的bean名称(将name前的&符号去除)mbd: 由此参数决定是从缓存中获取bean还是从FactoryBean中调用getObejct办法获取bean

    在创立FactoryBean的过程中, 因为name中蕴含了&符号, 则会在此代码中return, 最终将以后的FactoryBean返回实现bean的创立

    // 在获取的bean跟FactoryBean无关的case下. 后面这个条件永远不会满足,因为首先不论是获取FactoryBean还是FactoryBean保护的bean,从spring单例池中获取的bean肯定为FactoryBean。最终再依据理论要获取的bean的名称来决定返回的是FactoryBean还是FactoryBean保护的beanif (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {     return beanInstance; }

4.2 获取FactoryBean流程

  • 这里须要留神一个点: 不论是获取FactoryBean还是FactoryBean保护的bean。首先从spring单例池获取的bean都为FactoryBean,因为spring中没有一个bean会蕴含&符号,最终都是依据去除name中的&符号的beanName去获取bean。而后再依据以后bean的类型和name中是否蕴含&符号来确定返回的是FactoryBean还是FactoryBean保护的bean。所以咱们要想获取FactoryBean,要在beanName前增加&符号
  • 这里还要留神下, 要获取FactoryBean外部保护的一个对象是间接通过它的getObject办法获取的以及后续是从一个缓存中获取的,这个保护的bean并没有走spring的生命周期。也就是说,假如保护的那个bean实现了InitializingBean接口,然而并没有回调到afterPropertiesSet办法

4.3 获取FactoryBean保护的bean流程

  • 在下面的讲解下, 咱们要想获取到FactoryBean保护的bean,那就是beanName不能蕴含&符号, 由下面的demo运行后果也能发现

五、小结

  • 要获取FactoryBean则增加&符号。eg: context.getBean("&myFactoryBean")
  • 要获取FactoryBean保护的bean则不增加&符号。eg: context.getBean("myFactoryBean")
  • 在上篇博客中也有提到FactoryBean的一些规定,此链接:https://github.com/AvengerEug/spring/tree/develop/resourcecode-study#%E5%8D%81%E4%B8%80-%E8%8E%B7%E5%8F%96factorybean%E5%AE%9E%E4%BE%8B%E4%B8%8E%E5%AE%83%E7%BB%B4%E6%8A%A4%E7%9A%84bean%E5%AE%9E%E4%BE%8B%E8%A7%84%E5%88%99 ,本片博客是对上述链接的一个扩大,内容基本一致