7

Is there a way to auto-cast Spring beans to the class defined in the application context XML? I'd like to avoid putting type information about the beans in 2 places.... in the xml configuration file and also in the code as a cast.

For instance, given this config file

<bean id="bean-name" class="SimpleSpringBean"  scope="prototype">
    <property name="myValue" value="simple value"></property>
</bean>

Can I call ApplicationContext.getBean("bean-name") in such a way as to avoid directly casting the return type to SimpleStringBean. I know I can also call ApplicationContext.getBean("bean-name", SimpleSpringBean.class) to avoid the cast itself, but I still have the type info in 2 places.

It seems that Spring can get the class info (ApplicationContext.getType) or by getting the type from the bean itself, but no way to automatically cast the type without programmer intervention.

James McMahon
  • 48,506
  • 64
  • 207
  • 283
Vinnie
  • 12,400
  • 15
  • 59
  • 80
  • You're going to need the type information in two places either way because you need to declare the type of the variable: `SimpleStringBean var = ApplicationContext.getBean("bean-name");` But this looks like in breaks java: what type does `getBean(String)` return? – lmat - Reinstate Monica Dec 14 '12 at 17:04

6 Answers6

11

I agree with Sii, you should avoid calling getBean as much as you can. Just wire your beans to classes that depends on them.

Still, if you have a single class that holds the application context, you can provide a wrapper generic method like the following:

class MyContextHolder{
    ApplicationContext appContext;
    ......
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String beanName)
    {
        return (T)appContext.getBean(beanName);
    }
}

Then you can call it without casting

MyClass mc = MyContextHolder.getBean("myClassBean");
LiorH
  • 18,524
  • 17
  • 70
  • 98
  • 3
    Why is calling "getBean" considered bad? How else to you gain access to the Spring beans? – Vinnie May 01 '09 at 17:34
  • 5
    a. you encountered the casting problem b. your code has dependency on the spring framework c. you will probably use static methods to access the application context which will reduce your code testability d. the core idea behind spring framework is dependency injection, if bean A depends on bean B, spring can inject that dependency for you, no need to call getBean – LiorH May 01 '09 at 17:42
  • 1
    Basically, if you're bootstrapping an application yourself, you want to have a single class that you might get using getBean() in main, and through the dependencies you've configured, Spring builds your whole object graph with dependency injection. The whole point is for none of your classes to know or care how they get their dependencies, and if you're calling getBean(), they definitely do care! – ColinD May 01 '09 at 17:47
  • regarding d, I fully understand that I would use Spring to inject B into A, but I still need access to A as some point, right? How do I get that without calling getBean()? – Vinnie May 01 '09 at 17:48
  • Technically, you don't /really/ need to access A. Just make sure it's not initialized early using `lazy="false"` and define an init-method that will start your application. – millimoose May 01 '09 at 18:08
  • Also, Java throws an unchecked exception for a reason. The above code isn't typesafe, and just obscures the cast happening. – millimoose May 01 '09 at 18:10
  • *has "unchecked cast" warnings for a reason. SO won't let me delete and repost a comment on accepted answers... – millimoose May 01 '09 at 18:11
  • @LiorH, very useful suggestion to have the ContextHolder class. – SGB May 15 '13 at 15:09
4

The answer is you shouldn't be using ApplicationContext.getBean() at all if it's possible, and bear with the one place you have to in the bootstrap code. (Generally, you should never need to use getBean() outside of your application's entry points.)

Also, what you're asking is likely impossible in the Java language at all. Casting is a compile-time feature, combined with a runtime check. The return type of getBean() simply must be known at compile time. Even if Spring can determine the type of an object, it can't change its own method signatures at runtime.

Another thing is that even if this were possible, the feature wouldn't be all that useful. Because Spring AOP is implemented using dynamic proxies, you nearly always want Spring to hand you an instance of an interface the bean implements (which could be an AOP proxy), not of the implementation class.

millimoose
  • 39,073
  • 9
  • 82
  • 134
1

Another approach I also use is autowiring the bootstrapping class using:

public class Main {
    @Autowired FooFacade foo;
    @Autowired BarFacade bar;

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("appCtx.xml");
        AutowireCapableBeanFactory bf = ctx.getAutowireCapableBeanFactory();
        Object main = bf.createBean(Main.class, 
                                    AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT,
                                    false);
        ((Main) main).run();
    }

    private void run() {
        foo.doBootstrapStuff();
        bar.doMoreBootstrapStuff();
    }
}

(Code done from memory. Might only work if you have the Spring context configured to process wiring annotations, in that case making setters for foo and bar should work.)

millimoose
  • 39,073
  • 9
  • 82
  • 134
1

The main reason for getBean being untyped is the compatibility of Spring (up to version 2.5.x) with Java 1.4. Spring 3.0 will drop that and thus offer typed getBean method then.

Nevertheless you should avoid looking up beans directly and minimize its usage as far as possible.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
0

What if I use Spring as an object factory that my application uses extensively. That is, instead of writing a bunch of classes that already inherit or wrap around known Java classes I just decide to move all that into an xml file to cut down on lines of Java code. This will mean many lines of xml but I won't need to make skeleton Java classes which I inject with Spring or autowire. Thus making the lines of Java code less.

For this reason and still as I am new to Spring I have just as stated in previous posts used static Java methods which wrap around the getBeans().

I have worked with Spring but it is still new to me so forgive my question.

-1

"Because Spring AOP is implemented using dynamic proxies, you nearly always want Spring to hand you an instance of an interface the bean implements (which could be an AOP proxy), not of the implementation class"

So there is no way to get a dynamic proxy using getBean(), then whats the best practice if there are no interfaces and its a stand alone driver class to be executed?