开发者

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. 开发者_Go百科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
    }
}


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.


@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);
    }


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).
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜