简体   繁体   中英

How can I get a Spring bean injected in my custom Wicket model class?

In a custom Wicket class, not unlike the following, I'm using a service bean which should be injected by Spring, as defined with the SpringBean annotation (from the wicket-spring project).

public class ReportExportFileModel extends AbstractReadOnlyModel<File> {

    @SpringBean(name = "reportService")
    ReportService reportService;

    ReportDto reportDto;

    ReportExportFileModel(ReportDto reportDto) {
        this.reportDto = reportDto;
    }

    @Override
    public File getObject() {
        try {
            return reportService.generatePentahoReport(reportDto);
        } catch (ReportGenerationException e) {
           // ...
        }
    }
}

However, this doesn't work: reportService.generatePentahoReport() fails with NullPointerException, because the bean has not been injected by Spring for some reason .

Curiously, I've used the exact same Model code as anonymous inner class on a Wicket Page, and there was no problem there.

How can I fix this?

The wicket dependency-injection works with classes implementing IComponentInstantiationListener. These application-level listeners are called whenever a Component is instantiated. This is the hook used for dependency injection of components.

The model classes do not have such a mechanism in place. Any model can directly implement IModel so there is no abstract base class which can call the listeners, unlike Component.

I use the following base class for my injected models (Wicket 1.5):

public abstract class InjectedDetachableModel<T> extends LoadableDetachableModel<T> {

    public InjectedDetachableModel() {
        Injector.get().inject(this);
    }

    public InjectedDetachableModel(T a_entity) {
        super(a_entity);
        Injector.get().inject(this);
    }
}

Edit : Summary of relevant differences between 1.4 and 1.5, taken from Wicket 1.5 migration guide :

Wicket 1.4

@Override
protected void init()
{
    // initialize Spring
    addComponentInstantiationListener(new SpringComponentInjector(this, applicationContext));
}

and

InjectorHolder.getInjector().inject(Object object)

Wicket 1.5:

@Override
protected void init()
{
    // initialize Spring
    getComponentInstantiationListeners().add(new SpringComponentInjector(this, applicationContext))
}

and

Injector.get().inject(Object object)

Apparently Spring beans don't get automatically injected to other classes than Pages . I've run to this also with my custom WebRequestCycle class.

One easy solution is to trigger the injection manually using InjectorHolder.getInjector().inject(this) .

So, writing the constructor like this makes the model work as intended:

ReportExportFileModel(ReportDto reportDto) {
    this.reportDto = reportDto;
    InjectorHolder.getInjector().inject(this);
}

Edit : ah, right after posting this, I found another SO question with a more accurate explanation of what's going on:

@SpringBean works only in any subclass of Component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM