2017-10-30 7 views
0

FactoryBean이 초기화되기 전에 FactoryBean.getObject()이 호출되는 중대한 문제가 발생합니다. 나는 어떤 wierdness를 표시 작은 샘플을 만들었습니다. 더 큰 응용 프로그램으로 볼 때 정확한 문제는 아니지만 관련이 있다고 생각합니다. Spring 어플리케이션 컨텍스트 - FactoryBean이 초기화되기 전에 호출 된 FactoryBean.getObject()

은 가능성 순환 참조에 의해 발생 어디 4.2.4.RELEASE

SpringTestContext.xml

스프링 버전을 사용하고

  • node1 참조 node2
  • node2 참조 node1

,451,515,

<?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

동일한 문제를 발견 한 사람에게는 원형 의존성을 제거하여 해결했습니다. prev 속성의 경우 Node 대신 String을 설정하여이 작업을 수행했습니다.

예 : 나는 오히려이 초기화되기 전에 Factory.getObject()를 호출하는 것보다 CircularReferenceException의 형태를 던진 것으로 봄 예상했을 것이다

<bean id="node2" class="com.hsbc.gbm.dsl.dtm.test.SpringTest$NodeFactory"> 
    <property name="prevBeanId" value="node1" /> 
</bean> 

public static class NodeFactory implements FactoryBean<Node>, ... { 
     private Node next; 
     private String prevBeanId; 
     ... 
    } 

XML.

실제 응용 프로그램에서는 순환 참조가 예제 테스트 케이스의 두 개가 아닌 원에 포함 된 많은 콩을 보았기 때문에보기가 어려웠습니다.