tags:

views:

108

answers:

3

Hi all,

Basically, I have a class A which depends on class B which in turn depends on a spring managed bean C, but I dont want to keep B as a class variable just use B inside one method for some reason. My solution is to create a static method get() in B which returns a instance of B. Now the problem is, C is not injected to B correctly.

// A cannot have B as a class/field variable.

public class A {
    public void method(){
        // B.get() returns a instance of B, but this instance is not the 
        // instance that spring created, it is the static "instance" in B.

        B.get().doSomething();// ofcourse it throws out a nullpointer exception
    }
}

class B{
    @Resource(name = "c")
    private C c;

    private static B instance;


    public static B get() {
         return instance==null ? (instance=new B()) : instance;
    }

    public void doSomething(){
        c.toString();       // this line will break if c is not
                            // injected to the instance of b 
    }
}

@Service("c")
class C {            
}

How do I solve this problem??

A: 

You'll have to get the bean programmatically from the Spring ApplicationContext, either within A#method(), or in the constructor of A or its initialization method, and caching the Spring injected B instance.

Jack Leow
+2  A: 

The whole point of using Spring is it is a dependency injection framework, and you are hardcoding the dependency of B in A.

Try very hard not to do that. If you don't want to store B in an instance variable, pass it as an argument to the method.

If you are stubborn about doing that then you have to get an ApplicationContext and load it yourself. An alternative might be to have B implement InitializingBean and then have your afterPropertiesSet method register the current instance with a static instance variable.

Dean Povey
If you want to get an ApplicationContext, one way to do it is to implement the ApplicationContextAware interface.
Alex Beardsley
+1  A: 

There isn't a direct bit of spring to support this, but are are a couple of work arounds to solve the problem.

  1. grab hold of the ApplicationContext in B.get() and call getBean in order to get spring to construct B. Getting hold of the ApplicationContext is another mission as it is typically not held anywhere statically, have a look in the spring reference manual for 'dirty singleton'

  2. Let spring construct the bean and then keep a reference to it in the instance variable:

@Service('b') class B{ @Resource(name = "c") private C c;

private static B instance;

public B(){
    // sets the static here
    // not ideal...should use afterProperties set or what ever the Annotation equivalent is
    instance = this;
}

public static B get() {
     if( instance == null ){
          throw new IllegalStateException("errr...say something useful here")
     }
     return instance;
}

public void doSomething(){
    c.toString();       // this line will break if c is not
                        // injected to the instance of b 
}

}

I've seen 2. used a couple of times on various projects, it's not pretty and if you have a choice don't do it, just get spring to wire the whole app. But if you don't have a choice, it might be the least worst option.

Gareth Davis