我遇到了一些奇怪的问题,在FactoryBean.getObject()
初始化之前调用了FactoryBean
。我创建了一个小样本,显示出一些奇怪的东西。这不是我在更大的应用程序中看到的确切问题,但我认为它是相关的。
这可能是由循环引用所引起的
node1
引用node2
node2
引用node1
我正在使用春季版4.2.4.RELEASE
SpringTestContext.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
>
<bean id="node1" class="com.hsbc.gbm.dsl.dtm.test.SpringTest$NodeFactory">
<property name="next" ref="node2" />
</bean>
<bean id="node2" class="com.hsbc.gbm.dsl.dtm.test.SpringTest$NodeFactory">
<property name="prev" ref="node1" />
</bean>
</beans>
SpringTest.java
public class SpringTest {
public static class Node {
private final Node next;
private final Node prev;
public Node(Node prev, Node next) {
super();
this.prev = prev;
this.next = next;
}
}
public static class NodeFactory implements FactoryBean<Node>, BeanNameAware, ApplicationContextAware, InitializingBean {
private Node next;
private Node prev;
private String beanName;
private Node node;
private ApplicationContext applicationContext;
private boolean initialized;
public void setNext(Node next) {
this.next = next;
}
public void setPrev(Node prev) {
this.prev = prev;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Class<?> getObjectType() {
return Node.class;
}
@Override
public void afterPropertiesSet() throws Exception {
node = new Node(prev, next);
initialized = true;
}
@Override
public Node getObject() {
System.out.println(String.format("NodeFactory: getObject() beanName=%s, initialized=%s, applicationContext=%s",
beanName, initialized, applicationContext));
if (!initialized) {
throw new RuntimeException("NodeFactory not initialized before getObject() for " + beanName);
}
return node;
}
public boolean isSingleton() {
return true;
}
}
@Test
public void test() {
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("com/hsbc/gbm/dsl/dtm/test/SpringTestContext.xml");
appContext.start();
}
}
输出
NodeFactory: getObject() beanName=null, initialized=false, applicationContext=null
异常
Caused by: java.lang.RuntimeException: NodeFactory not initialized before getObject() for null
at com.foo.SpringTest$NodeFactory.getObject(SpringTest.java:74)
at com.foo.SpringTest$NodeFactory.getObject(SpringTest.java:1)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
... 53 more
完整追踪
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node1' defined in class path resource [com/foo/SpringTestContext.xml]: Cannot resolve reference to bean 'node2' while setting bean property 'next'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node2' defined in class path resource [com/foo/SpringTestContext.xml]: Cannot resolve reference to bean 'node1' while setting bean property 'prev'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node1': FactoryBean threw exception on object creation; nested exception is java.lang.RuntimeException: NodeFactory not initialized before getObject() for null
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1481)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1226)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:753)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
at com.foo.SpringTest.test(SpringTest.java:117)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node2' defined in class path resource [com/foo/SpringTestContext.xml]: Cannot resolve reference to bean 'node1' while setting bean property 'prev'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node1': FactoryBean threw exception on object creation; nested exception is java.lang.RuntimeException: NodeFactory not initialized before getObject() for null
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1481)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1226)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 38 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'node1': FactoryBean threw exception on object creation; nested exception is java.lang.RuntimeException: NodeFactory not initialized before getObject() for null
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:127)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1585)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 48 more
Caused by: java.lang.RuntimeException: NodeFactory not initialized before getObject() for null
at com.foo.SpringTest$NodeFactory.getObject(SpringTest.java:74)
at com.foo.SpringTest$NodeFactory.getObject(SpringTest.java:1)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
... 53 more
答案 0 :(得分:0)
对于任何发现相同问题的人,我通过删除循环依赖来解决这个问题。我通过为Node
属性设置字符串而不是prev
来完成此操作。
例如:
public static class NodeFactory implements FactoryBean<Node>, ... {
private Node next;
private String prevBeanId;
...
}
XML
<bean id="node2" class="com.hsbc.gbm.dsl.dtm.test.SpringTest$NodeFactory">
<property name="prevBeanId" value="node1" />
</bean>
我原本以为Spring会抛出某种形式的CircularReferenceException
而不是在Factory.getObject()
初始化之前调用它。
在我的实际应用程序中,循环引用更难以看到,因为圆圈中涉及许多bean而不是示例测试用例中的两个bean。