4

I have a problem with injecting a bean into a helper class. It works basically like this: I create an object in the page constructor that does some work, returns some data and I show these on the page. In this helper object, a service should be injected via @Autowired annotation. However, I always get a null pointer exception when I use it. I also tried @SpringBean but it didn't help. On the other hand, when I inject this service directly into the page with @SpringBean, it's accessible and works fine. Do you know where the problem is?

This is the page:

public class Page extends BasePage {
    public Page() {
        HelperObject object = new HelperObject(new Application("APP_NAME"));
        String result = object.getData();

        add(new Label("label", result));
    }
}

Helper object:

public class HelperObject {
    private Application app;

    @Autowired
    private Service service;

    public HelperObject(Application app) {
        this.app = app;
    }

    public String getData() {
        // use service, manipulate data, return a string
    }
}
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
John Manak
  • 13,328
  • 29
  • 78
  • 119

3 Answers3

5

You can inject dependencies into non-Spring-non-Wicket-new-created objects using @SpringBean by calling InjectorHolder.getInjector().inject(this); in its constructor.

For example:

class MyPojo {
    @SpringBean
    MyDumbDAO dao;
    MyPojo() {
        InjectorHolder.getInjector().inject(this);
    }
    void justDoIt() {
        dao.duh(); // dao is there!
    }
}

Note that it will only work if called within a Wicket-managed request. If not (ie, if it's a Quartz job, or a Filter executed before Wicket's), the Application instance will not be available, and the injector won't know how to get the dependencies.

Another solution is to use Spring's @Configurable. It uses AspectJ to intercept creation of annotated objects, and inject its dependencies, even if you instantiate them directly with new (or some other framework, like Hibernate, creates them internally). But this requires runtime or build-time (works better for me) bytecode manipulation, which may be too much magic for some people.

tetsuo
  • 10,726
  • 3
  • 32
  • 35
3

The best practice would be to create your objects via a factory bean (that has those properties injected by Spring, and have that factory inject those properties to objects it spawns - pure IoC).

You should really avoid using SpringContext all over the place (or any other similar solution for that matter). Here is a partial list of reasons:

  1. Your code gets coupled with Spring way too much (low-cohesion).
  2. You mix plumbing code with the business-logic.
  3. Your code is less readable.
  4. It's less maintainable (e.g., changing the name of the service bean would lead to code modification - this violates SRP & OCP).
  5. It's less testable (e.g., you need the Spring framework to test it).
Moshe Bixenshpaner
  • 1,840
  • 1
  • 17
  • 23
3

@SpringBean only injects dependencies into classes that inherit from Wicket's Component. @Autowired only injects dependencies into classes created by Spring itself. That means you can't automatically inject a dependency into an object you create with new.

(Edit: you can also add a @SpringBean injection to your class by injecting in the constructor: InjectorHolder.getInjector().inject(this);)

My normal workaround for this is to use my application class to help. (I'm a little puzzled by your use of new Application(...). I assume this isn't actually org.apache.wicket.Application.) For example:

public class MyApplication extends AuthenticatedWebApplication implements
    ApplicationContextAware {

    private ApplicationContext ctx;

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

    public static MyApplication get() {
        return (MyApplication) WebApplication.get();
    }

    public static Object getSpringBean(String bean) {
        return get().ctx.getBean(bean);
    }

    public static <T> T getSpringBean(Class<T> bean) {
        return get().ctx.getBean(bean);
    }

    ....
}

In my Spring application context:

<!-- Set up wicket application -->
<bean id="wicketApplication" class="uk.co.humboldt.Project.MyApplication"/>

My helper object then looks up the service on demand:

public class HelperObject {

    private Service getService() {
        return MyApplication.getSpringBean(Service.class);
    }
Adrian Cox
  • 6,204
  • 5
  • 41
  • 68
  • Thanks, that's what I thought after having played with it for a while. Is there a way to inject dependencies (using annotations) into objects that I create with `new...`? Yes, you're right, my `Application` object is a different one from the Wicket Application object. – John Manak Mar 24 '11 at 12:36
  • Dependency injection needs some way to get hold of your newly created object. You can do this the Wicket way by inheriting from a base class which handles injection, or by having Spring create the object using a prototype scope: http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-scopes-prototype – Adrian Cox Mar 24 '11 at 12:42
  • You should also be careful about keeping references to service objects in objects that Wicket will serialise in the session. See this answer: http://stackoverflow.com/questions/4544802/wicket-springbean-doesnt-create-serializable-proxy/4737788#4737788 – Adrian Cox Mar 24 '11 at 12:45
  • Also added an example of the `InjectorHolder` in constructor approach. – Adrian Cox Mar 24 '11 at 12:49