spring 5.0.x源码学习系列九: FactoryBean和BeanFactory

前言

  1. bean: 通过spring创建,走了spring的bean创建过程
  2. 普通对象: new出来的,未经过spring的bean创建过程

一、项目demo

1.1 项目包结构

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第1张图片

1.2 AppConfig.java

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第2张图片

1.3 BeanA.java

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第3张图片

1.4 Entry.java

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第4张图片

1.5 MyFactoryBean.java

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第5张图片

二、运行结果

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第6张图片

三、原理解析

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

3.1.1 创建FactoryBean入口

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第7张图片

3.1.2 处理bean名称和第一次getSingleton

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第8张图片

3.1.3 创建完FactoryBean

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第9张图片

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
    spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第10张图片
3.2.1.1获取MyFactoryBean情况: context.getBean("&myFactoryBean")

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第11张图片

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

spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第12张图片
spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第13张图片

  • 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) 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方法中新增一行代码

      spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第14张图片

    spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第15张图片spring 5.0.x源码学习系列九: FactoryBean和BeanFactory_第16张图片

    四、流程总结

    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运行结果也能发现

    五、小结

    你可能感兴趣的:(javaspring)