2017-07-08 34 views
1

当使用Spring并结合ProxyFactoryBean和@Inject Provider <>时,会在启动过程中创建大量对象。Spring BeanFactory和类型搜索

我已经确定原因为DefaultListableBeanFactory.doGetBeanNamesForType方法。通过迭代所有的bean定义并搜索可以满足提供程序泛型参数的定义,可以满足“@Inject Provider <>”。当遇到FactoryBean时,它首先被完全初始化,然后才被查询getObjectType()。但是,ProxyFactoryBean通常在applicationContext.xml中设置,并依赖于它们代理的bean。完全初始化ProxyFactoryBean会导致内部bean的实例化。

所有这一切都很好,除非内部bean在那个时候不能被实例化 - 例如,因为它依赖于一些其他的bean,直到原始的bean(提供者的那个)才能被初始化。没有循环依赖性,只有过度渴望的初始化。

实施例:

class Bean1 { @Inject Provider<X> provider;} 
class Bean2 { @Inject Bean1 bean1;} 

applicationContext.xml: 
<bean id="bean1" class="com.rb.springissues.sample.Bean1"/> 
<bean id="bean2" class="com.rb.springissues.sample.Bean2"/> 
<bean id="bean2Factory" class="org.springframework.aop.framework.ProxyFactoryBean"> 
    <property name="target" ref="bean2"/> 
    <property name="proxyTargetClass" value="true"/> 
</bean> 

在上述例子中的流动是(由弹簧完全管理):

  • 实例化bean1。尝试初始化bean1并发现它有Provider<X>
    • 在上下文中遍历所有BeanDefinition。对于每个人:
      • 如果它是“普通”bean,请评估类以查看它是否合适。
      • 如果它是FactoryBean,请尝试实例化并初始化FactoryBean以查看“getObjectType()”将返回的内容。
        • 为了初始化bean2Factory,需要给它提供一个bean2的实例。所以Spring试图实例化并初始化bean2
          • 但是 - bean2无法初始化,因为它依赖于bean1 - 导致Spring引发循环依赖性错误。
      • 如果FactoryObject已正确创建,现在春天请求的类型和缓存响应(“好路”)。
      • 如果我们遇到了一个异常(循环依赖错误),它会被捕获并被忽略,但结果不会被缓存 - 所以如果我们得到另一个bean,它会再次做同样的事情(并且一次又一次...)

参见https://github.com/bironran/spring_issues_proxy_factory为全面描述和样品。

我观察到约500个定义的bean实例化并尝试初始化超过300,000个对象(同一个bean一次又一次)由于此问题的实际应用程序。该创业公司被分钟推迟和GC高峰。

此外,此问题呈指数级增长 - 任何无法解决的新依赖项都可能使应用程序加载的时间加倍。

很想听听关于如何解决的建议(参见github项目)。

+0

你可以陈述你的商业案例,那就是你想要解决的问题吗?我没有看到它在这里或你的GitHub页面。在尝试解决它带来的问题之前,也许我们可以退后一步并质疑ProxyFactoryBean的用法。当然,除非这纯粹是一项学术活动。 –

+1

我们使用ProxyFactoryBean来应用性能监控,限制,事务管理,客户选择(作为多客户SaaS产品)并应用权限(在特定情况下)。 –

+0

那么基本上,为了交叉的关切?我猜想还有其他的方法可以做,但是可以。 –

回答

1

解决方案竟然是容易 - 更换<property name="target" ref="bean2"/>有: <property name="targetName"> <idref bean="bean2"/> </property> <property name="targetClass" value="com.rb.springissues.sample.Bean2"/>

这导致bean2的延迟绑定 - 这样的工厂可以在没有Bean2实例进行初始化。

+0

感谢您发布更新。对不起,我帮不了你;不再有调试XML配置的欲望了。似乎回到过去。 –