smalllove 2020-02-11
目录
Spring 系列目录:https://www.cnblogs.com/binarylei/p/10198698.html
本章讨论的范围:AbstractFactoryBean 创建单例 Bean 过程中出现的循环依赖问题,多例 Bean 的创建根本不会出现循环依赖,因为会直接抛异常。
在 AbstractFactoryBean 中有一个特殊的属性 earlySingletonInstance,用于提前将 bean 暴露。第一次看到这个属性很奇怪:
带着疑问,我们先看一下代码:
private T singletonInstance; # FactoryBean创建的对象bean private T earlySingletonInstance; # 提前暴露bean,用于解决循环依赖
AbstractFactoryBean 初始化完成后调用 afterPropertiesSet 创建对象,然后通过 getObject 获取对象。
@Override public void afterPropertiesSet() throws Exception { if (isSingleton()) { this.initialized = true; this.singletonInstance = createInstance(); this.earlySingletonInstance = null; } } @Override public final T getObject() throws Exception { if (isSingleton()) { // 单例,解决循环依赖,先生成代理对象,当getObject时才生成对象 return (this.initialized ? this.singletonInstance : getEarlySingletonInstance()); } else { // 多例,不会解决循环依赖,直接报错 return createInstance(); } }
说明: 正常流程下,AbstractFactoryBean 初始化完成后,调用 afterPropertiesSet 创建对象,此时 initialized = true 且对象创建完成,当 getObject 时直接返回 singletonInstance。什么情况下 initialized 会为 false 呢?
我的第一想法是,这两个 FactoryBean 相互依赖,形成 AB - BA 的关系,但心里也有点惴惴不安,因为 Spring IoC 应该已经解决了这类循环依赖的问题,不管怎样,先进行一番实验再说。
class FactoryBeanA extends AbstractFactoryBean<BeanA> { @Override public Class<?> getObjectType() { return BeanA.class; } @Override protected BeanA createInstance() throws Exception { return new BeanAImpl(); } } class FactoryBeanB extends AbstractFactoryBean<BeanB> { @Override public Class<?> getObjectType() { return BeanB.class; } @Override protected BeanB createInstance() throws Exception { return new BeanBImpl(); } } class BeanAImpl implements BeanA { } class BeanBImpl implements BeanB { } interface BeanA { } interface BeanB { }
说明: 因为 AbstractFactoryBean 通过 getEarlySingletonInstance 生成的代理对象是通过 JDK 的动态代理生成的,所以 BeanAImpl 和 BeanBImpl 必须有接口。
猜想1:两个 FactoryBean 直接互相依赖
在 FactoryBeanA 和 FactoryBeanB 中添加如下代码,FactoryBeanA 和 FactoryBeanB 创建时形成相互依赖关系:
class FactoryBeanA extends AbstractFactoryBean<BeanA> { @Autowired private FactoryBeanB factoryBeanB; } class FactoryBeanB extends AbstractFactoryBean<BeanA> { @Autowired private FactoryBeanA factoryBeanA; }
说明: 果不其然,容器运行时根本没有调用到 getEarlySingletonInstance() 方法,也就是没有出现 FactoryBean 还未初始化完成就需要调用 getObject 创建对象的情况,当然 FactoryBeanA 和 FactoryBeanB 创建过程肯定是有循环依赖的。
猜想2:两个 FactoryBean#getObject 互相依赖
在 FactoryBeanA 和 FactoryBeanB 中添加如下代码:
class FactoryBeanA extends AbstractFactoryBean<BeanA> { @Autowired private BeanB beanB; } class FactoryBeanB extends AbstractFactoryBean<BeanA> { @Autowired private BeanA beanA; }
说明: 果然,容器运行时调用到 getEarlySingletonInstance() 方法。也就是出现了 FactoryBean 还未初始化完,但要调用 getObject 创建对象的情况,这是肯定不行了,这就是循环依赖产生的根据原因,如何解决呢?
总结: 两个猜想说明
说明: AbstractFactoryBean 循环依赖原因分析
当然,我们很容易想到,像 Spring IoC 一样,如果我们在 FactoryBeanA#getObject 时,创建一个代理对象 earlySingletonInstance 提前暴露到出去,这样 FactoryBeanA#getObject 不就可以正常初始化了吗。事实上 AbstractFactoryBean 也正是这么做的。
说明: 从时序图可以看到
我们看一下 getEarlySingletonInstance 方法,会生成一个代理对象 earlySingletonInstance,当调用 getObject 时会直接将这个代理对象返回。
private T getEarlySingletonInstance() throws Exception { Class<?>[] ifcs = getEarlySingletonInterfaces(); if (ifcs == null) { throw new FactoryBeanNotInitializedException( getClass().getName() + " does not support circular references"); } if (this.earlySingletonInstance == null) { this.earlySingletonInstance = (T) Proxy.newProxyInstance( this.beanClassLoader, ifcs, new EarlySingletonInvocationHandler()); } return this.earlySingletonInstance; }
说明: earlySingletonInstance 通过 JDK 的动态代理生成代理对象,所以如果 FactoryBean 要支持循环依赖,必须有接口。
到目前为止,似乎一切顺利,FactoryBean 的循环依赖通过提前暴露动态代理对象,避免了死循环,但真的没有问题了吗?
你可能对下面这段代码感到困惑,因为本章中我们讨论的都是单例 Bean 的问题,怎么可能不相等呢?
@Test public void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( CircularFactoryBeanDemo.class); BeanA beanA = context.getBean(BeanA.class); FactoryBeanB factoryBeanB = context.getBean(FactoryBeanB.class); Assert.assertNotEquals(beanA, factoryBeanB.getBeanA()); }
说明: 首先 earlySingletonInstance 是 JDK 代理对象,singletonInstance 是真正的实例,它们俩肯定不是一个东西。EarlySingletonInvocationHandler 正是代理了 singletonInstance 对象,即 earlySingletonInstance 底层其实也是使用 singletonInstance,这样就保证了数据的一致性,并不违反单例原则。
private class EarlySingletonInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // getSingletonInstance() 直接返回了 singletonInstance return method.invoke(getSingletonInstance(), args); } }
说明: 既然 earlySingletonInstance 底层实际上也是使用 singletonInstance 对象,那至少数据肯定是一致的。
下图显示了这两个 beanA 之间的关系:
还有什么问题?现在我们已经知道 earlySingletonInstance 和 singletonInstance 是代理关系,我们如果想获取 singletonInstance 的注解等信息,可能通过正常的途径就无法获取了。特别是现在元编程大行其道的情况下,我们很多时候都需要获取类的注解、方法参数等信息,只能通过原始的对象类型获取。此时要特别小心。
每天用心记录一点点。内容也许不重要,但习惯很重要!