Results 1 to 8 of 8

Thread: Circular Dependencies

  1. #1

    Default Circular Dependencies

    Hi,

    I a problem where I would like to have two service beans with cross reference to each other.

    In my case, personServiceBean needs orgServiceBean and orgServiceBean needs personServiceBean.

    I have declared these as properties and declare everything in my XML file, but when I run my application I get a "org.springframework.beans.factory.FactoryBeanCirc ularReferenceException".

    Is there a way to get around this problem?

  2. #2
    Join Date
    Aug 2004
    Location
    u.s.a
    Posts
    399

    Default

    Would you need a third bean that references the other two?

  3. #3

    Default

    No.

  4. #4
    Join Date
    Aug 2004
    Location
    Southampton, UK
    Posts
    826

    Default

    Can you post the code for config and the structural code of the beans please?

  5. #5

    Default

    This is the bean definition. The serviceProxyTemplate is a TransactionProxyFactoryBean.

    Code:
        <bean id="personService" parent="serviceProxyTemplate">
    		<property name="target">
    			<bean class="biz.ist.atlas.service.PersonService"  >
                    <property name="orgService">
                        <ref bean="organisationService"/>
                    </property>
    			</bean>
    		</property>
    	</bean>
    
    	<bean id="organisationService" parent="serviceProxyTemplate">
    		<property name="target">
    			<bean class="biz.ist.atlas.service.OrganisationService">
    				<property name="personService">
    					<ref bean="personService"/>
    				</property>
    			</bean>
    		</property>
    	</bean>
    This is the error message:

    Error creating bean with name 'biz.ist.atlas.service.PersonService' defined in resource [/WEB-INF/applicationContext.xml] of ServletContext: Can't resolve reference to bean 'organisationService' while setting property 'orgService'; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'biz.ist.atlas.service.OrganisationService' defined in resource [/WEB-INF/applicationContext.xml] of ServletContext: Can't resolve reference to bean 'personService' while setting property 'personService'; nested exception is org.springframework.beans.factory.FactoryBeanCircu larReferenceException: Error creating bean with name 'personService': FactoryBean returned null object: not fully initialized due to circular bean reference

  6. #6
    Join Date
    Aug 2004
    Location
    Southampton, UK
    Posts
    826

    Default

    I see now. Unfortunately, because of the way FactoryBeans work they need to be fully initializaed before they can be used to create the actual bean. Since a circular reference between two FactoryBeans will always prevent both from being initialized it is not permitted.

    To get around this, have one of your beans implement ApplicationContextAware, this way you can look up the bean using ApplicationContext.getBean() yourself. Alternatively, you just wire up one of the dependencies using DI and then create a BeanFactoryPostProcessor to wire up the other for you after both FactoryBeans have been created. This is a slightly more complex solution, but is a little bit more elegant.

    Rob

  7. #7
    Join Date
    Nov 2004
    Location
    Austin, Texas USA
    Posts
    46

    Default

    Quote Originally Posted by robh
    I see now. Unfortunately, because of the way FactoryBeans work they need to be fully initializaed before they can be used to create the actual bean. Since a circular reference between two FactoryBeans will always prevent both from being initialized it is not permitted.
    Sadly, I have this same problem when only using one FactoryBean...

    Code:
    ...
        <bean id="lookupTarget" class="com.company.commons.persist.HibernateDataCallback">
            <property name="sessionFactory">
                <ref local="sessionFactory"/>
            </property>
            <property name="transformHibernateCollections">
                <value>true</value>
            </property>
            <property name="interceptor">
                <bean class="com.company.commons.persist.audit.AuditInterceptor">
                    <property name="dataCallback">
                        <ref local="dataCallback"/>
                    </property>
                </bean>
            </property>
        </bean>
    
        <bean id="dataCallback" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager">
                <ref local="transactionManager"/>
            </property>
            <property name="target">
                <ref local="lookupTarget"/>
            </property>
            <property name="transactionAttributes">
                <props>
                    <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                    <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                    <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                    <prop key="store*">PROPAGATION_REQUIRED</prop>
                </props>
            </property>
        </bean>
    
    ...
    Causes the following exception...

    Error creating bean with name 'dataCallback': FactoryBean returned null object: not fully initialized due to circular bean reference
    org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'com.company.commons.persist.audit.AuditIntercepto r' defined in class path resource


    Is this also not possible? I'm trying to set a Hibernate Interceptor (which is NOT a FactoryBean) on my HibernateDataCallback, which is exposed as a TransactionProxyFactoryBean. The interceptor requires a reference to the dataCallback itself so that it can add or remove rows during hibernate transactions...

    Thanks! Keep up the great work!
    -Alex

  8. #8
    Join Date
    Jan 2005
    Posts
    2

    Default One possible solution

    I had the same problem. And while I'd like to eliminate the circular dependency, it's not really an option given my timeframe. Here is the solution I came up with. It's not all that pretty, but it seems to work for what I'm doing at the moment.

    NOTE: I initially tried just setting the reference directly by calling getBean in the postProcessBeforeInitialization method, but the problem is that if you do that you still run into the circular dependency. The workaround is just to create a dynamic proxy that performs the bean lookup for every method invocation. It's a slight performance hit, and could have some implications if you're not using singleton services, but otherwise it should work.

    Code:
    public class CircularReferenceConfigurer
    	implements BeanFactoryPostProcessor, BeanPostProcessor &#123;
    
    	private String _sourceBeanName;
    	private String _sourceBeanPropertyName;
    	private String _targetBeanName;
    	private String _targetBeanInterface;
    	private BeanFactory _factory;
    
    	public CircularReferenceConfigurer&#40;&#41; &#123;
    	&#125;
    
    	public void postProcessBeanFactory&#40;ConfigurableListableBeanFactory factory&#41;
    		throws BeansException &#123;
    
    		if &#40;_sourceBeanName == null&#41; &#123;
    			throw new BeanCreationException&#40;"sourceBeanName must be defined"&#41;;
    		&#125;
    		if &#40;_sourceBeanPropertyName == null&#41; &#123;
    			throw new BeanCreationException&#40;"sourceBeanPropertyName must be defined"&#41;;
    		&#125;
    		if &#40;_targetBeanName == null&#41; &#123;
    			throw new BeanCreationException&#40;"targetBeanName must be defined"&#41;;
    		&#125;
    		if &#40;_targetBeanInterface == null&#41; &#123;
    			throw new BeanCreationException&#40;"targetBeanInterface must be defined"&#41;;
    		&#125;
    		_factory = factory;
    	&#125;
    
    	public Object postProcessBeforeInitialization&#40;Object bean, String beanName&#41;
    		throws BeansException &#123;
    		return bean;
    	&#125;
    
    	public Object postProcessAfterInitialization&#40;Object bean, String beanName&#41;
    		throws BeansException &#123;
    
    		// If the bean created is not equal to the source bean, don't do anything
    		if &#40;!_sourceBeanName.equals&#40;beanName&#41;&#41; &#123;
    			return bean;
    		&#125;
    
    		// Create the dynamic proxy for the target bean
    		Class targetInterface = null;
    		try &#123;
    			targetInterface = Class.forName&#40;_targetBeanInterface&#41;;
    		&#125; catch &#40;ClassNotFoundException e1&#41; &#123;
    			throw new BeanCreationException&#40;
    				"unable to locate targetBeanInterface &#91;"
    					+ _targetBeanInterface
    					+ "&#93;",
    				e1&#41;;
    		&#125;
    		Object targetBeanProxy =
    			BeanFactoryBeanProxy.newInstance&#40;
    				targetInterface,
    				_factory,
    				_targetBeanName&#41;;
    
    		// Now set the dependency
    		try &#123;
    			BeanInfo beanInfo = null;
    			beanInfo = Introspector.getBeanInfo&#40;bean.getClass&#40;&#41;&#41;;
    			PropertyDescriptor&#91;&#93; propertyDescriptors =
    				beanInfo.getPropertyDescriptors&#40;&#41;;
    			for &#40;int i = 0; i < propertyDescriptors.length; i++&#41; &#123;
    				if &#40;_sourceBeanPropertyName
    					.equals&#40;propertyDescriptors&#91;i&#93;.getName&#40;&#41;&#41;&#41; &#123;
    					Method setter = propertyDescriptors&#91;i&#93;.getWriteMethod&#40;&#41;;
    					setter.invoke&#40;bean, new Object&#91;&#93; &#123; targetBeanProxy &#125;&#41;;
    					return bean;
    				&#125;
    			&#125;
    			throw new BeanCreationException&#40;
    				"no such property &#91;"
    					+ _sourceBeanPropertyName
    					+ "&#93; found for object&#58; "
    					+ bean&#41;;
    		&#125; catch &#40;IllegalAccessException e&#41; &#123;
    			throw new BeanCreationException&#40;
    				"unable to set property &#91;"
    					+ _sourceBeanPropertyName
    					+ "&#93; on sourceBean &#91;"
    					+ _sourceBeanName
    					+ "&#93;",
    				e&#41;;
    		&#125; catch &#40;InvocationTargetException e&#41; &#123;
    			throw new BeanCreationException&#40;
    				"unable to set property &#91;"
    					+ _sourceBeanPropertyName
    					+ "&#93; on sourceBean &#91;"
    					+ _sourceBeanName
    					+ "&#93;",
    				e&#41;;
    		&#125; catch &#40;IllegalArgumentException e&#41; &#123;
    			throw new BeanCreationException&#40;
    				"unable to set property &#91;"
    					+ _sourceBeanPropertyName
    					+ "&#93; on sourceBean &#91;"
    					+ _sourceBeanName
    					+ "&#93;",
    				e&#41;;
    		&#125; catch &#40;IntrospectionException e&#41; &#123;
    			throw new BeanCreationException&#40;
    				"unable to set property &#91;"
    					+ _sourceBeanPropertyName
    					+ "&#93; on sourceBean &#91;"
    					+ _sourceBeanName
    					+ "&#93;",
    				e&#41;;
    		&#125;
    	&#125;
    
    	public String getSourceBeanName&#40;&#41; &#123;
    		return _sourceBeanName;
    	&#125;
    	public String getTargetBeanName&#40;&#41; &#123;
    		return _targetBeanName;
    	&#125;
    	public void setSourceBeanName&#40;String string&#41; &#123;
    		_sourceBeanName = string;
    	&#125;
    	public void setTargetBeanName&#40;String string&#41; &#123;
    		_targetBeanName = string;
    	&#125;
    	public String getSourceBeanPropertyName&#40;&#41; &#123;
    		return _sourceBeanPropertyName;
    	&#125;
    	public void setSourceBeanPropertyName&#40;String string&#41; &#123;
    		_sourceBeanPropertyName = string;
    	&#125;
    	public String getTargetBeanInterface&#40;&#41; &#123;
    		return _targetBeanInterface;
    	&#125;
    	public void setTargetBeanInterface&#40;String string&#41; &#123;
    		_targetBeanInterface = string;
    	&#125;
    
    	private static class BeanFactoryBeanProxy implements InvocationHandler &#123;
    
    		private BeanFactory _factory;
    		private String _beanName;
    
    		private BeanFactoryBeanProxy&#40;BeanFactory factory, String beanName&#41; &#123;
    			_factory = factory;
    			_beanName = beanName;
    		&#125;
    
    		public static Object newInstance&#40;
    			Class theInterface,
    			BeanFactory factory,
    			String beanName&#41; &#123;
    			Class&#91;&#93; proxyInterfaces = new Class&#91;&#93; &#123; theInterface &#125;;
    			return Proxy.newProxyInstance&#40;
    				theInterface.getClassLoader&#40;&#41;,
    				proxyInterfaces,
    				new BeanFactoryBeanProxy&#40;factory, beanName&#41;&#41;;
    		&#125;
    
    		public Object invoke&#40;Object proxy, Method m, Object&#91;&#93; args&#41;
    			throws Throwable &#123;
    			Object result;
    			try &#123;
    				// Before the method, lookup the target bean
    				Object target = _factory.getBean&#40;_beanName&#41;;
    				result = m.invoke&#40;target, args&#41;;
    			&#125; catch &#40;InvocationTargetException e&#41; &#123;
    				throw e.getTargetException&#40;&#41;;
    			&#125; catch &#40;Exception e&#41; &#123;
    				throw e;
    			&#125; finally &#123;
    				// After the method has completed, but before the argument
    				// is returned
    			&#125;
    			return result;
    		&#125;
    
    	&#125;
    
    &#125;

    Then in your configuration file, you use it as follows:

    Code:
    	<bean id="fooService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    		<property name="target">
    			<bean class="fooServiceImpl">
    				<property name="barService">
    					<ref bean="barService" />
    				</property>
    			</bean>
    		</property>
    	</bean>
    
    	<bean id="barService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    		<property name="target">
    			<bean id="barServiceImpl" class="barServiceImpl">
    				<!-- circular dependency
    					<property name="fooService">
    					<ref bean="fooService" />
    					</property>
    				-->
    			</bean>
    		</property>
    	</bean>
    
    	<bean id="barServiceRefConfigurer" class="CircularReferenceConfigurer">
    		<property name="sourceBeanName">
    			<value>barServiceImpl</value>
    		</property>
    		<property name="sourceBeanPropertyName">
    			<value>fooService</value>
    		</property>
    		<property name="targetBeanName">
    			<value>fooService</value>
    		</property>
    		<property name="targetBeanInterface">
    			<value>FooService</value>
    		</property>
    	</bean>

Similar Threads

  1. Circular Dependencies
    By samokk in forum Container
    Replies: 4
    Last Post: Aug 4th, 2005, 12:38 PM
  2. could not satisfy dependencies
    By springuser in forum Container
    Replies: 4
    Last Post: Apr 26th, 2005, 01:15 PM
  3. Replies: 1
    Last Post: Apr 25th, 2005, 07:37 PM
  4. Replies: 1
    Last Post: Jan 14th, 2005, 11:34 PM
  5. Circular Dependency Nightmare
    By pburleson in forum Container
    Replies: 2
    Last Post: Nov 11th, 2004, 01:13 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •