Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
339 views
Welcome To Ask or Share your Answers For Others

1 Answer

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();
        ...
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...