简体   繁体   English

使用Hibernate 4的Integrator模式和Spring的依赖注入

[英]Using Hibernate 4's Integrator pattern and Spring's dependency injection

I'm used to using Spring to do my dependency injection like so: 我习惯使用Spring来执行依赖注入,如下所示:

<context:component-scan base-package="org.emmerich.myapp" />

and then annotating my dependent classes with Autowired like so: 然后使用Autowired注释我的依赖类,如下所示:

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. 但是,随着Hibernate 4.0的变化,我们现在建议使用新的Integrator接口进行服务发现。 This includes adding event listeners for triggers such as postUpdate , postDelete etc. 这包括为postUpdatepostDelete等触发器添加事件侦听postDelete

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 . 我已定义的集成器将我的监听器添加到ServiceFactory This is referenced in the file META-INF/services/org.hibernate.integrator.spi.Integrator . 这在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. 我还定义了MyListener类,它看起来像是典型的事件监听器。

@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. 我想这是因为我在实例MyListener内部MyIntegrator ,它不拿起成分,不会自动装配组件。 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() . 首先,使用Spring必须执行new MyListener()时感觉不对。 I expect to be able to define that as an autowired dependency and have Spring create a singleton for me. 我希望能够将其定义为自动连接的依赖项,并让Spring为我创建一个单例。 My question is this: 我的问题是:

What's the best approach to using dependency injection with the new Integrator interface? 在新的Integrator接口中使用依赖注入的最佳方法是什么? 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. 集成器用于构建SessionFactory,因此当他们被要求集成自己时,我猜没有可用的应用程序上下文。 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. 因此,我在Integrator中需要的任何bean都需要以“老式”方式创建,并且不会在它们上接收自动装配。

I'm quite new to the world of Spring, would you say this is something that I should expect to see? 我对Spring这个世界很陌生,你会说这是我应该期待看到的吗? 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 ? 我知道当我在SessionFactory时,我处于应用程序的不同范围,但有没有办法获得对bean的引用并启用autowire,即使我是通过new创建它?

The solution I came up with used ApplicationContextAware . 我提出的解决方案使用了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. 这意味着只要上下文可用, MyListener收到对ApplicationContext的引用,并且我在方法调用的上下文中引用了bean,而不是bean构造。 Creating a bean with new doesn't limit this, so Spring still gives me the application context: new创建bean并不限制它,所以Spring仍然给我应用程序上下文:

@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? 有没有更好的办法?

As stated in the comment i went another way of integrating Spring managed HibernateEventListeners. 正如评论中所述,我采用了另一种集成Spring托管的HibernateEventListeners的方法。 Here's the code: 这是代码:

The identifier interface for Spring managed Hibernate event listeners: Spring托管的Hibernate事件侦听器的标识符接口:

public interface HibernateEventListener { }

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

During an upgrade from hibernate 3.6 to 4.2, we needed to have a custom validator that uses spring-managed beans by doing the following configuration: 在从hibernate 3.6升级到4.2期间,我们需要通过执行以下配置来使用使用spring-managed bean的自定义验证器:

<!-- Make our validators use DI if necessary -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <!-- other props -->
    <property name="hibernateProperties">
            <map>
                <entry key="javax.persistence.validation.factory" value-ref="validator" />
            </map>
    </property>
</bean>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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