简体   繁体   中英

Wiring MVP with Spring IOC without circular reference?

In my MVP applications I use code such as the following to wire my Presenter and View:

View view = new View();
Presenter presenter = new Presenter(view);
view.setPresenter(presenter);

The View class is constructed in a temporarily invalid state, which the call to setPresenter rectifies. I have some code in the View class that throws an IllegalStateException if the View is used without the Presenter being configured.

I was hoping Spring could wire this relationship together with a configuration such as:

<bean id="presenter" class="com.foo.Presenter">
  <constructor-arg ref="view" />
</bean>

<bean id="view" class="com.foo.View">
  <property name="presenter" ref="presenter" />
</bean>

This failed with a lengthy circular-dependency exception.

Is there a way I can tell Spring to construct the view bean, then construct the presenter bean before finally calling the setter on view ?


A related question is Spring setter dependency injection after all beans have been created . However, one of the suggested solutions is to resolve the circular dependencies by using setter-based wiring, which is exactly what I'm failing to do here. The latest manual also seems to agree - see the box entitled "Circular dependencies":

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection .

I'm sure that there is a better solution, but if all else fails you can do it "manually":

Configuration:

<bean id="presenter" class="com.foo.Presenter">
</bean>

<bean id="view" class="com.foo.View" init-method="init">
</bean>

View class:

public class View implements ApplicationContextAware {

    private ApplicationContext applicationContext;
    private Presenter presenter;

    public void init(){
        presenter = (Presenter)applicationContext.getBean("presenter");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

As an extra note, if you have annotation-driven on your configuration you can just do @Autowired private ApplicationContext applicationContext; instead of implementing the ApplicationContextAware interface.

Some further research has unearthed a solution. Initially, I tried reversing the order of the bean definitions in the XML config file and it worked:

<bean id="view" class="com.foo.View">
  <property name="presenter" ref="presenter" />
</bean>

<bean id="presenter" class="com.foo.Presenter">
  <constructor-arg ref="view" />
</bean>

However, this felt wrong as I'm confident I shouldn't be relying on file ordering to ensure things aren't breaking. This then led to the realisation that the depends-on can solve the problem:

<bean id="presenter" class="com.foo.Presenter" depends-on="view">
  <constructor-arg ref="view" />
</bean>

<bean id="view" class="com.foo.View">
  <property name="presenter" ref="presenter" />
</bean>

I welcome comments on whether this is a good approach. It's quite plausible I'm bending Spring to my will in a way that's not intended.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM