Spring配置为单例的Service15通过FactoryBean创建却被初始化两次原因排查
问题成因
- Spring 中实现了
FactoryBean接口的类,其生产的单例对象会在getObject()方法正常返回后,才被存入Spring内部缓存,后续对该bean的请求才会直接复用缓存实例。 - 你的代码存在循环依赖场景:
Service16依赖Service15实例,而Service15Factory的getObject()方法中又主动调用beanFactory.getBean("service16")获取Service16实例。 - 执行流程触发两次
getObject()调用:- 容器初始化
Service16时,需要注入Service15属性,触发第一次getObject()调用 getObject()内部执行getBean("service16")时,Service16还未完成属性注入,会再次触发Service15的获取请求- 此时第一次
getObject()还未执行完成,Spring缓存中还没有Service15实例,因此会第二次调用getObject(),创建第二个Service15实例
- 容器初始化
修复方案
方案1:自行缓存Service15实例
在Service15Factory中自行实现实例缓存,避免重复创建,改造成本最低:
public class Service15Factory implements FactoryBean, BeanFactoryAware { private BeanFactory beanFactory; // 缓存生产的Service15实例 private Service15 service15; public Service15Factory(){ System.out.println("serviceFactory15 init"); } @Override public Object getObject() throws Exception { // 仅首次调用时创建实例 if (service15 == null) { beanFactory.getBean("service16"); service15 = new Service15(); } return service15; } @Override public Class<?> getObjectType() { return Service15.class; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
方案2:调整Service16的获取时机
将Service16的获取逻辑从getObject()中移出,改为通过Spring注入的方式提前获取,更符合Spring使用规范:
修改Service15Factory代码:
public class Service15Factory implements FactoryBean { private Service16 service16; private Service15 service15; public Service15Factory(){ System.out.println("serviceFactory15 init"); } @Override public Object getObject() throws Exception { if (service15 == null) { service15 = new Service15(); } return service15; } @Override public Class<?> getObjectType() { return Service15.class; } // 新增set方法,让Spring注入service16 public void setService16(Service16 service16) { this.service16 = service16; } }
修改XML配置,增加service16的注入声明:
<bean id="service15" class="com.myspring.service.Service15Factory" scope="singleton"> <property name="service16" ref="service16"/> </bean> <bean id="service16" class="com.myspring.service.Service16"> <property name="service15" ref="service15"></property> </bean>
方案3:调整业务逻辑消除循环依赖
如果业务逻辑允许,直接移除getObject()中的beanFactory.getBean("service16")调用,从根源上避免循环获取场景。
内容的提问来源于stack exchange,提问作者chenzy
相关产品推荐
相关产品推荐

