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?
spring-context and spring-tx for core Spring functionalities. Notice we are using version 4.0.3.RELEASE. spring-orm dependency for Spring ORM support, it’s required for hibernate integration in our spring project. hibernate-entitymanager and hibernate-core dependencies for Hibernate framework.
The Spring documentation recommends using constructor-based injection for mandatory dependencies, and setter-based injection for optional ones. 7. Field-Based Dependency Injection In case of Field-Based DI, we can inject the dependencies by marking them with an @Autowired annotation:
spring-orm dependency for Spring ORM support, it’s required for hibernate integration in our spring project. hibernate-entitymanager and hibernate-core dependencies for Hibernate framework. Notice that version is 3.6.9.Final, Incase if you are using Hibernate 4 we need is to change it to 4.3.5.Final as commented in above pom.xml file.
The design principle of Inversion of Control emphasizes keeping the Java classes independent of each other and the container frees them from object creation and maintenance. These classes, managed by Spring, must adhere to the standard definition of Java-Bean. Dependency Injection in Spring also ensures loose-coupling between the classes.
As stated in the comment i went another way of integrating Spring managed HibernateEventListeners. Here's the code:
The identifier interface for Spring managed Hibernate event listeners:
public interface HibernateEventListener { }
The HibernateIntegrator:
@Service
public class HibernateSpringIntegrator {
private static final Logger log = LoggerFactory.getLogger(HibernateSpringIntegrator.class);
@Autowired
private HibernateEntityManagerFactory entityManagerFactory;
@Autowired
private HibernateSpringIntegratorRegistry hibernateSpringIntegratorRegistry;
@PostConstruct
public void registerListeners() {
log.debug("Registering Spring managed HibernateEventListeners");
EventListenerRegistry listenerRegistry = ((SessionFactoryImpl) entityManagerFactory
.getSessionFactory()).getServiceRegistry().getService(
EventListenerRegistry.class);
List<HibernateEventListener> eventListeners = hibernateSpringIntegratorRegistry
.getHibernateEventListeners();
for (HibernateEventListener hel : eventListeners) {
log.debug("Registering: {}", hel.getClass());
if (PreInsertEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_INSERT,
(PreInsertEventListener) hel);
}
if (PreUpdateEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_UPDATE,
(PreUpdateEventListener) hel);
}
if (PreDeleteEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.PRE_DELETE,
(PreDeleteEventListener) hel);
}
if (PostInsertEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_INSERT,
(PostInsertEventListener) hel);
}
if (PostUpdateEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_UPDATE,
(PostUpdateEventListener) hel);
}
if (PostDeleteEventListener.class.isAssignableFrom(hel.getClass())) {
listenerRegistry.appendListeners(EventType.POST_DELETE,
(PostDeleteEventListener) hel);
}
// Currently we do not need other types of eventListeners. Else this method needs to be extended.
}
}
}
The "Registry":
@Component
public class HibernateSpringIntegratorRegistry {
@Autowired(required = false)
private List<HibernateEventListener> hibernateEventListeners;
public List<HibernateEventListener> getHibernateEventListeners() {
if (hibernateEventListeners == null) {
return Collections.emptyList();
}
return hibernateEventListeners;
}
}
And here's an example implementation:
@Component
public class MailGenerationEventListener implements HibernateEventListener,
PostDeleteEventListener, PostInsertEventListener, PostUpdateEventListener {
@Override
public void onPostDelete(PostDeleteEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
@Override
public void onPostInsert(PostInsertEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
@Override
public void onPostUpdate(PostUpdateEvent event) {
Class<?> entityClass = event.getEntity().getClass();
...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With