【问题标题】:Using Hibernate 4's Integrator pattern and Spring's dependency injection使用 Hibernate 4 的 Integrator 模式和 Spring 的依赖注入
【发布时间】:2013-04-07 20:11:30
【问题描述】:

我习惯于像这样使用 Spring 进行依赖注入:

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

然后用 Autowired 注释我的依赖类,如下所示:

public class DependentClass {

    @Autowired
    private Dependency dependency;

}

但是,随着 Hibernate 4.0 中的更改,我们现在建议使用新的Integrator 接口进行服务发现。这包括为 postUpdatepostDelete 等触发器添加事件侦听器。

不幸的是,这不能很好地与通过注释依赖项进行的依赖项注入配合使用。我有以下设置:

我定义的集成器将我的侦听器添加到ServiceFactory。这在文件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);

}

我还定义了 MyListener 类,它看起来像您的典型事件侦听器。

@Component
public class MyListener implements PostInsertEventListener {

    @Autowired
    private Dependent dependent;

    public void onPostInsert(PostInsertEvent event) {
         // dependent == null
    }

}

不幸的是,如评论所示,这不起作用。我想这是因为我在MyIntegrator 内部实例化MyListener,它不会拾取组件并且不会自动装配组件。但是,如果我尝试这样做:

@Component
public class MyIntegrator {

     @Autowired
     private MyListener listener;

     ...
}

那么监听器不会自动连接。

首先,在使用 Spring 时不得不做new MyListener() 感觉不对。我希望能够将其定义为自动装配的依赖项,并让 Spring 为我创建一个单例。我的问题是这样的:

在新的 Integrator 接口中使用依赖注入的最佳方法是什么? Integrator 用于构建 SessionFactory,因此当他们被要求集成自己时,我想没有可用的应用程序上下文。因此,我在 Integrator 中需要的任何 bean 都需要以“老式”方式创建,并且不会接收到它们的自动装配。

我对 Spring 的世界还很陌生,你认为这是我应该期待看到的吗?我知道当我在 SessionFactory 中时,我处于应用程序的不同范围内,但是即使我是通过 new 创建它,是否有办法获取对 bean 的引用并启用自动装配?

我想出的解决方案使用了ApplicationContextAware。这意味着只要上下文可用,MyListener 就会收到对 ApplicationContext 的引用,并且我在方法调用而不是 bean 构造时从上下文中引用了 bean。使用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);
    }

}

有没有更好的办法?

【问题讨论】:

  • 您没有忘记在您的@Autowired 示例中对MyIntegrator 类进行组件扫描?另外,谁在实例化 MyIntegrator 类?
  • 这也是我想出的完全相同的解决方案。它工作得很好,但静态应用程序上下文感觉有点脏:)我只是没有找到更好的方法,因为休眠正在创建集成器。一旦我有时间,我想让它更抽象,并拥有一个集成器,它只是一个链接,能够注册由 spring 创建的侦听器等。我曾经找到一个博客,但我现在不记得网址了。
  • @SotiriosDelimanolis Hibernate 为您创建Integrator。您在 META-INF/services 目录中指定一个文本文件,指向您想要实例化的集成器列表,Hibernate 在创建 SessionFactory 之前为您构建它们。我有点理解无法在 Integrator 中自动装配组件 - 它是在应用程序范围之外构建的。但是,我认为可以在侦听器中使用@Autowired。是的,我正在对所有涉及的类进行上下文扫描。
  • @MartinFrey 是的,我知道你的意思。我确实考虑过只有一个集成器,但感觉不对。 Hibernate 似乎希望您指定各种集成器来装饰您的SessionFactory。如果你得到博客链接,请告诉我。
  • 我刚刚重新检查了我的实现,我终于走了另一条路。基本上我在我的集​​成器中自动装配 HibernateEntityManagerFactory 并使用 serviceregistry 来实现我的侦听器。这种方式完全像Spring。我发布我的代码作为答案。

标签: java spring hibernate spring-mvc inversion-of-control


【解决方案1】:

如评论中所述,我采用了另一种集成 Spring 管理的 HibernateEventListeners 的方式。代码如下:

Spring 管理的 Hibernate 事件监听器的标识符接口:

public interface HibernateEventListener { }

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.
        }
    }
}

“注册表”:

@Component
public class HibernateSpringIntegratorRegistry {

    @Autowired(required = false)
    private List<HibernateEventListener> hibernateEventListeners;

    public List<HibernateEventListener> getHibernateEventListeners() {
        if (hibernateEventListeners == null) {
            return Collections.emptyList();
        }
        return hibernateEventListeners;
    }
}

这是一个示例实现:

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

【讨论】:

  • 有趣的回应。这是实现Integrator 接口并在META-INF/services 中定义积分器文本文件的替代方法吗?
  • 我想我遇到的问题是它不遵循 Integrator 模式,而是提供了一种自定义的 Springified 附加事件侦听器的方式。
  • 是的,这是一个完全 Springifed 的实现。我认为可以采用 ApplicationContextAware 将 HibernateSpringIntegrator 实现为“真正的”集成器服务并获取 HibernateSpringIntegratorRegistry 的方式。这样,集成器就像静态应用程序上下文引用一样休眠,但仍然能够通过注册表为事件侦听器使用简单的自动装配
  • 马丁,你能举一个你上面提到的例子吗?我设置了一个集成器来记录休眠保存、更新和删除。我想扩展它并将此信息保存到基于 spring 安全主体(登录用户)的数据库中。我很困惑如何从积分器模式访问该对象。可能吗?还是我应该实施像您上面发布的解决方案?
  • @blong824 我没有完整的示例,但基本上您可以实现一个 ApplicationContextAware 类,其中包含对 applicationContext 的静态引用。在集成器中,您引用此类并使用该静态引用来获取事件侦听器并绑定它们。
【解决方案2】:

在从 hibernate 3.6 升级到 4.2 的过程中,我们需要通过执行以下配置来拥有一个使用 spring 管理的 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>

【讨论】:

  • 这有什么关系?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
  • 2011-02-12
  • 1970-01-01
相关资源
最近更新 更多