Spring BeanFactory和类型搜索

时间:2017-07-08 23:57:01

标签: java spring

当使用Spring并将ProxyFactoryBean和@Inject Provider<>结合使用时,最终会在启动时创建大量对象。

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

所有这一切都很好,除非当时内部bean无法实例化 - 例如因为它依赖于一些其他bean,直到原始bean(具有Provider的那个)之前无法初始化。没有循环依赖,只有过度的初始化。

示例:

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>

在上面的例子中,流程是(完全由Spring管理):

  • 实例化bean1。尝试初始化bean1并发现它有Provider<X>
    • 浏览上下文中的所有BeanDefinition。每个:
      • 如果它是&#34;正常&#34; bean,评估该类是否适合。
      • 如果它是FactoryBean,请尝试实例化并初始化FactoryBean以查看&#34; getObjectType()&#34;会回来的。
        • 为了初始化bean2Factory,需要使用bean2的实例。所以Spring尝试实例化并初始化bean2
          • 但是 - bean2无法初始化,因为它依赖于bean1 - 导致Spring抛出循环依赖性错误。
      • 如果FactoryObject被正确创建,Spring现在要求输入类型并缓存响应(&#34;良好路径&#34;)。
      • 如果我们得到一个异常(循环依赖性错误),它被捕获并被忽略,但结果没有被缓存 - 所以如果我们得到另一个bean,它会再次做同样的事情(一次又一次......)

有关完整说明和示例,请参阅https://github.com/bironran/spring_issues_proxy_factory

我观察到实际应用程序有大约500个已定义的bean实例化,并尝试初始化超过300,000个对象(一次又一次地使用相同的bean),因为这个问题。启动时间延迟了几分钟,GC峰值。

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

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

1 个答案:

答案 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实例的情况下初始化工厂。