javamonkey79
javamonkey79

Reputation: 17775

chicken and egg Spring bean binding

Ok, so here is my use case:

I have the following classes each encapsulating an instance of the next one in line. So:

A -> B -> C -> D

eg: In class A, I have an instance of class B and in class B I have an instance of C and so on.

Well, I am trying to convert the loading\initialization\injection logic to a hybrid Spring system. The general idea being that B, C and D will need to more or less be ApplicationContextAware. By that I mean, that they will not actually implement that interface, but instead require the ApplicationContext as a constructor parameter. This way, in the hybrid approach (where the developer does not use Spring to initialize the instance) they must at least pass in the ApplicationContext so that additional beans may be wired. The problem is, that in order for the Spring container to load the beans, I now have to pass in the ApplicationContext in the XML. But as far as I can tell, there is no nice way to do this.

I tried something like this:

public class ApplicationContextPlaceholder implements ApplicationContextAware {

    private ApplicationContext _applicationContext;

    public void setApplicationContext( final ApplicationContext applicationContext ) throws BeansException {
        _applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return _applicationContext;
    }

}

<bean id="a" class="com.company.A">
    <constructor-arg>
        <bean id="applicationContext" class="com.company.ApplicationContextPlaceholder" />
    </constructor-arg>
</bean>

But obviously this doesn't make any sense, since ApplicationContextPlaceholder isn't really an ApplicationContext. I've also looked for ways to reference the context inside the XML, but I'm not finding anything.

Does anyone know of an elegant solution to this type of problem?

EDIT #1:

I was thinking about it, and I could have ApplicationContextPlaceholder also implement ApplicationContext and just delegate to the injected context and then it occurred to me that maybe, just maybe this was already in Spring...but as far as I can tell, no.

EDIT #2:

The reason each class needs an ApplicationContext is that if a dev wishes to override one of the classes in the chain (say, C for sake of argument). In this instance the child class of C will still need to load D via Spring.

Upvotes: 1

Views: 435

Answers (2)

Go Dan
Go Dan

Reputation: 15502

Unless a class is providing additional plumbing functionality, you should avoid exposing the ApplicationContext. Quoting the Spring reference: in general you should avoid it, because it couples the code to Spring and does not follow the Inversion of Control style.

If you are providing additional functionality (maybe, for example, a factory class that uses the ApplicationContext to assemble objects), then it's prudent to implement ApplicationContextAware since your functionality is already tied to Spring.

If you've considered your dependency injection alternatives and have decided on injecting the ApplicationContext in your beans, your ApplicationContextPlaceholder class (I would stay away from a Placeholder prefix to avoid any confusion with Spring property placeholders) is certainly a solution. (Since it is your own class, why not extend ApplicationObjectSupport for additional functionality.)

This class will need to be defined and initialized in your configuration, for example:

<bean id="appCtxHolder" class="ApplicationContextHolder" />

Because ApplicationContextHolder implements ApplicationContextAware, Spring will inject the ApplicationContext into appCtxHolder when it is initialized. You can use it for constructor injection like:

<bean id="a" class="com.company.A">
    <constructor-arg>
        <bean factory-bean="appCtxHolder" factory-method="getApplicationContext" />
    </constructor-arg>
</bean>

Upvotes: 1

aishwarya
aishwarya

Reputation: 1986

ApplicationContextPlaceholder could have everything static. In that case, you don't need to pass ApplicationContext around, when a API requests for a certain bean, you could check if its null and if it is, load it using the ApplicationContext from ApplicationContextPlaceholder. That's assuming a setter based injection, if you are doing constructor based, you could init beans in the contructor too.

Upvotes: 0

Related Questions