Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting dependencies using @Autowired into objects created with "new ..."

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
    }
}
like image 324
John Manak Avatar asked Dec 22 '22 16:12

John Manak


2 Answers

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.

like image 71
tetsuo Avatar answered Dec 29 '22 08:12

tetsuo


@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);
    }
like image 33
Adrian Cox Avatar answered Dec 29 '22 08:12

Adrian Cox