I'm used to using Spring to do my dependency injection like so:
<context:component-scan base-package="org.emmerich.myapp" />
and then annotating my dependent classes with Autowired
like so:
public class DependentClass {
@Autowired
private Dependency dependency;
}
However, with the changes in Hibernate 4.0, we're now advised to use the new Integrator
interface for service discovery. This includes adding event listeners for triggers such as postUpdate
, postDelete
etc.
Unfortunately, this doesn't play nicely with dependency injection through annotated dependencies. I have the following setup:
An integrator I have defined to add my listener to the ServiceFactory
. This is referenced in the file META-INF/services/org.hibernate.integrator.spi.Integrator
.
public class MyIntegrator implements Integrator {
private MyListener listener;
public MyIntegrator() {
listener = new MyListener();
}
@Override
public void integrate(Configuration configuration,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry eventRegistry =
serviceRegistry.getService(EventListenerRegistry.class);
eventRegistry.prependListeners(EventType.POST_COMMIT_INSERT, listener);
}
I also have defined the class MyListener
, which looks like your typical event listener.
@Component
public class MyListener implements PostInsertEventListener {
@Autowired
private Dependent dependent;
public void onPostInsert(PostInsertEvent event) {
// dependent == null
}
}
Unforunately, as shown by the comment, this doesn't work. I guess it's because I'm instantiating MyListener
inside MyIntegrator
, it doesn't pick up the component and doesn't autowire components. However, if I try this:
@Component
public class MyIntegrator {
@Autowired
private MyListener listener;
...
}
Then the listener isn't autowired.
Firstly, it feels wrong whilst using Spring to have to do new MyListener()
. I expect to be able to define that as an autowired dependency and have Spring create a singleton for me. My question is this:
What's the best approach to using dependency injection with the new Integrator interface? The Integrators are used to build a SessionFactory, and so when they're asked to integrate themselves I guess there isn't an application context available. Because of that, any beans I require in the Integrator need to be created the "old fashioned" way and won't receive the autowiring on them.
I'm quite new to the world of Spring, would you say this is something that I should expect to see? I understand that I'm in a different scope of the application when I'm in the SessionFactory, but is there a way to obtain a reference to the bean and enable autowire even though I'm creating it via new
?
The solution I came up with used ApplicationContextAware
. It meant that MyListener
received a reference to the ApplicationContext
whenever the context was available, and I referenced the beans from the context on method calls, rather than on bean construction. Creating a bean with new
doesn't limit this, so Spring still gives me the application context:
@Component
public class MyListener implements PostInsertEventListener, ApplicationContextAware {
private static ApplicationContext context;
public void onPostInsert(PostInsertEvent event) {
// getDependent() == correct!
}
public void setApplicationContext(ApplicationContext context) throws BeanException {
this.context = context;
}
public Dependent getDependent() {
return context.getBean(Dependent.class);
}
}
Is there a better way?
See Question&Answers more detail:
os