Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage of custom scopes breaks functioning of circular references [SPR-5955] #10623

Closed
spring-issuemaster opened this issue Jul 24, 2009 · 5 comments
Closed

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Jul 24, 2009

Dirk Scheffler opened SPR-5955* and commented

The following construct brings up a BeanCurrentlyInCreationException.

 
<bean name="A" class="foo.bar.X" scope="session">
  <property name="refToB" ref="B"/>
</bean>

<bean name="B" class="foo.bar.Y" scope="session">
  <property name="refToA" ref="a"/>
</bean>

As the example shows clearly, there are no problematic references via constructor-arg involved and the same construct but with scope="singleton" does actually work. I see no reason why circular references should not be possible for beans that have a custom scope. Therefor this seems clearly a bug.


Affects: 3.0 M3

1 votes, 3 watchers

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 24, 2009

Juergen Hoeller commented

I'm afraid I have to disagree: By design, Spring supports circular references for singleton beans only. That's simply a known limitation.

We deliberately chose to not support circular references for scoped beans when we introduced them back in Spring 2.0. Personally, if I got to choose again, I would stick with this decision and not even support circular references for singletons beans either. Circular references introduce more problems that they solve, even for singletons. Some party always gets a not-quite-fully initialized reference there. Anyway, let's leave the well-known circular reference discussion aside and focus on potential solutions...

The underlying reason for the limitation with scoped beans is the Scope SPI. The get operation accepts an ObjectFactory and provides atomicity guarantees, i.e. it never returns a half-initialized object. The scope backend may even build those objects on remote servers or the like, returning a serialized form of it, or it may return a proxy to a cluster-wide shared object. (Custom scopes are used for those kinds of things as well, e.g. with Terracotta as a backend.) In such a scoping scenario, circular dependencies are a no-go to begin with.

So what can we do about it? We could define a new optional operation for Scope implementations, exposing a reference while the object is still being initialized. That's not quite trivial in the details for several reasons (such as doing it on demand only, as well as dealing with clustering side effects of premature HttpSession.setAttribute calls) but doable in principle, at least for request and session scope. We're going to revisit this for Spring 3.1, next to conversation management which happens to be a related topic anyway.

Juergen

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 24, 2009

Dirk Scheffler commented

Thank you for your fast reply and the respect you pay to the issue.

Actually my sample was kept simple to show the effect. In my project I use an own custom scope but the the effect is the same. So I would really appreciate to have a way to support circular references and also would do extra effort in my custom scope implementation for it. I think there are scopes where it does not make sense to support circular references and there are scopes where it isn't even possible - so it depends on the implementation. This would be great for me.

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Sep 23, 2009

Dirk Scheffler commented

Dear Juergen,

I like to ask again if this could be done for spring 3.1.

It would be very nice if it would be possible to support circular references for all custom scope. It is no problem iI this would mean some extra effort for the custom scope implementor. I have own custom scopes (beside session, request) that I want to work with circular references.

Probably we can also think together about what can be done to deal with the problem of the imcompleteness of some circular referenced bean. Are you interested in such a communication? But this would be just for the future. For now we should accept this problem, because in GUI environments the circular references come in by adding event handlers that will on react after the system has been built up completely.

What do you think?

@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Nov 15, 2009

Dirk Scheffler commented

A preliminary solution for the problem

I was able to develop a preliminary solution that works for me and like to share it with the others that think circular references are a natural phenomenon and cannot be left out only because it brings technical problems. This solutions enables circular references for beans of any scope type. Normally circular references are only supported for beans of the normal singleton scope. Please be aware that you will not get fully initialized beans if you use circular references.

Of course my solution could have problems and I would like to know if you find anything.

The solution consists of two parts:

  1. An InstantiationAwareBeanPostProcessor implementation (named EarlyReferenceFixBeanPostProcessor) which must be registered for an ApplicationContext in your xml.
  2. An subclassed BeanFactory implementation (I show it here for GenericApplicationContext)
public class EarlyReferenceFixBeanPostProcessor 
    extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {

  private BeanFactory beanFactory;
  
  private static Map<BeanFactory, Map<String, Object>> earlyReferences = new HashMap<BeanFactory, Map<String,Object>>();

  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
  }
  
  public static Map<String, Object> getEarlyReferences(BeanFactory beanFactory) {
    Map<String, Object> refs = earlyReferences.get(beanFactory);
    if (refs == null) {
      refs = new HashMap<String, Object>();
      earlyReferences.put(beanFactory, refs);
    }
    return refs;
  }
  
  public static Object getEarlyReference(BeanFactory beanFactory, String beanName) {
    return getEarlyReferences(beanFactory).get(beanName);
  }
  
  protected Map<String, Object> getEarlyReferences() {
    return getEarlyReferences(beanFactory);
  }
  
  @Override
  public boolean postProcessAfterInstantiation(Object bean, String beanName)
      throws BeansException {
    getEarlyReferences().put(beanName, bean);
    return super.postProcessAfterInstantiation(bean, beanName);
  }
  
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    getEarlyReferences().remove(beanName);
    return super.postProcessBeforeInitialization(bean, beanName);
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<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-2.5.xsd
  ">
  
  <bean class="org.springframework.beans.factory.config.EarlyReferenceFixBeanPostProcessor"/>
  
</beans>
public static void main(String[] args) {
  GenericApplicationContext ctx = new GenericApplicationContext(new DefaultListableBeanFactory(){
    @Override
    protected Object doGetBean(String name, Class requiredType,
        Object[] args, boolean typeCheckOnly)
        throws BeansException {
      String beanName = transformedBeanName(name);
      
      Object bean = EarlyReferenceFixBeanPostProcessor.getEarlyReference(this, beanName);
      if (bean != null)
        return bean;
      else
        return super.doGetBean(name, requiredType, args, typeCheckOnly);
    }
  });
}
@spring-issuemaster
Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Sep 22, 2015

Juergen Hoeller commented

Closing groups of outdated issues. Please reopen if still relevant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.