当使用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项目)。
你可以陈述你的商业案例,那就是你想要解决的问题吗?我没有看到它在这里或你的GitHub页面。在尝试解决它带来的问题之前,也许我们可以退后一步并质疑ProxyFactoryBean的用法。当然,除非这纯粹是一项学术活动。 –
我们使用ProxyFactoryBean来应用性能监控,限制,事务管理,客户选择(作为多客户SaaS产品)并应用权限(在特定情况下)。 –
那么基本上,为了交叉的关切?我猜想还有其他的方法可以做,但是可以。 –