tags:

views:

927

answers:

3

I have an application that has multiple screens and each screen is selected via a button. Each screen contains pretty heavy-weight components so it's important that only the activate screen is in memory - all others should be available for garbage collection.

The app uses Spring for the glue and currently it switches screens using getBean():

//event handler for a specific button
public void actionPerformed(Event e) {
    setScreen( (Screen) applicationContext.getBean("screen1"));
}

"screen1" is a prototype bean so a new instance of the screen is created when the button is pressed. Also, setScreen() is the only place where a reference to Screen is maintained in the app so the previously active screen will be available for garbage collection. I haven't tested this yet but I expect it will work fine - no rocket science here!

The problem is - after reading this page about why getBean() is considered bad - I'm wondering whether there is a more idiomatic way to acheive the same results while removing the dependency on getBean().

I have looked at method injection and it looks to me to introduce complexity with little benefit. It's another concept to learn, more magic, adds dependency on CGLIB, etc. If I really want to remove the dependency on Spring, I can just introduce an interface that exposes the getBean() method.

Is getBean() and method injection the only options in my case or have I missed something?

And if so, is getBean() really so bad?

A: 

Have you considered a factory approach?

public interface ComponentFactory<T> {
  T create();
}

public class ScreenFactory implements ComponentFactory<Screen> {
  @Override
  Screen create() { ... }
}

public class MyApp {
  private ComponentFactory<Screen> screen1;

  public void actionPerformed(Event e) {
    setScreen(screen1.create());
  }

  public void setScreen1(ComponentFactory<Screen> screen1) {
    this.screen1 = screen1;
  }

  private void setScreen(Screen screen) { ... }
}

combined with:

<bean id="screenFactory" class="com.myclass.ScreenFactory"/>

<bean id="myapp" class="...">
  <property name="screen1" ref="screenFactory"/>
</bean>

You can of course auto-wire the above.

The problem with what you're doing is that it both hardcodes the bean you're instantiating and ties your implementation to ApplicationContext. If you ever need to mock and/or unit test your apps/components, then that'll make your life incredibly difficult. The factory solution above will make it trivial.

cletus
So in your solution, getbean() is invoked within the factory create() method, right?
Paul Drummond
Not exactly. It's replaced by a factory method but it's a lot easier to mock the above interface than it is to mock an application context. Plus it's pluggable. Ultimately, it is a somewhat simpler implementation of Spring's method injection http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-lookup-method-injection
cletus
I see, but where does the factory create() method get its Screen instance from?
Paul Drummond
+1  A: 

Setter injection, property injection or constructor injection all create a loosely coupled application that is much easier to test through mocking. It also prevents any of your classes from having direct dependencies on Spring (or other IoC container) classes. It ends up just being a cleaner overall solution when you don't have to manually call getBean().

I think you should become comfortable with the notion of configuring dependencies. The "magic" is not really magic at all and is just something that you will become comfortable with as you use it.

geofflane
I am referring to method injection using the lookup-method tag which IMO is different to "standard" Dependency Injection - it's a separate feature and one which relies on AOP (and therefore CGLIB) and what does it give you over a factory (which cletus has demonstrated?)
Paul Drummond
The factory will isolate your dependency on Spring, but it becomes what I think is just unnecessary code to maintain. What's wrong with AOP? AOP is just an implementation detail of how Spring does DI.
geofflane
A: 

If you just want to get rid of the getBean, consider using a ServiceLocatorFactoryBean. But how well this works for you may depend on where the string "screen1" is coming from in your application.

Ed Thomas