前言
- 上篇博客spring 5.0.x源码学习系列八: 实例化bean之使用构造方法创建bean、自动装配与循环依赖主要介绍了Spring bean的实例化过程,包括自动装配和依赖注入。其中有提到
FactoryBean
这个字眼,FactoryBean
是一种特殊的bean,它可以维护两个bean并都交由spring管理。但BeanFactory它并不是一个bean,是一个普通对象,通过new
关键字创建的。this.beanFactory = new DefaultListableBeanFactory();
- 这里先解释下bean和普通对象的概念
-
bean
: 通过spring创建,走了spring的bean创建过程 -
普通对象
: 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包含两种含义
- 类型不是FactoryBean类型的bean
- 用户的目的就是想要获取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")
-
在main方法中新增一行代码
四、流程总结
4.1 创建FactoryBean流程
- 判断当前创建bean的类型为FactoryBean, 在bean名称前添加&符号
- 进入doGetBean方法, 对bean名称前面的&符号进行清除,内部维护了两个bean名称,一个是name另一个是beanName。其中name为真正创建的bean名称,beanName是处理过的bean名称。最终会以beanName去创建bean,所以FactoryBean对应的bean的名称就是首字母小写
- 创建完bean后统一走getObjectFromFactoryBean方法。在此方法中有四个参数都比较重要:
在创建FactoryBean的过程中, 因为name中包含了&符号, 则会在此代码中return, 最终将当前的FactoryBean返回完成bean的创建beanInstance: 在doGetBean获取到的bean或者创建出来的bean对象 name: 要获取真正bean的名称 beanName: 处理过的bean名称(将name前的&符号去除) mbd: 由此参数决定是从缓存中获取bean还是从FactoryBean中调用getObejct方法获取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 ,本片博客是对上述链接的一个扩展,内容基本一致