乐趣区

关于spring:spring-50x源码学习系列九-FactoryBean和BeanFactory

前言

  • 上篇博客 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 保护的 bean
    if (!(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,本片博客是对上述链接的一个扩大,内容基本一致
退出移动版