前言

Hibernate 提供许多集成点,允许集成新功能或为标准功能提供自定义行为。本指南涵盖了这些集成点,并面向将

  • 将 Hibernate 集成到 Java EE 应用程序服务器、Spring 框架、缓存解决方案(例如 Infinispan、Ehcache、Hazelcast)中的软件开发人员和架构师。

  • 提供自定义集成

  • 希望覆盖标准功能

1. 服务和注册表

服务和注册表从 4.0 开始作为正式概念出现。但不同服务提供的功能实际上在 Hibernate 中存在了很久。新的变化是通过一个轻量级的专用容器(我们称之为 ServiceRegistry)来管理它们、它们的生存周期和依赖关系。本指南的目的是描述这些 ServicesRegistries 的设计和目的,并在适当的情况下查看其实现细节。它还将深入探讨第三方集成商和应用程序如何利用和自定义 ServicesRegistries

1.1. 什么是服务?

服务以可插拔的方式提供特定类型的功能。具体来说,它们是定义特定功能的接口,然后是这些 Service 契约接口的实现。接口称为 Service 角色;实现类称为 Service 实现。可插拔性来自这样一个事实:Service 实现遵守由 Service 角色接口定义的契约,并且 Service 的使用者针对 Service 角色而不是实现进行编程。

一般来说,用户可以插入所有标准 Service 角色的备选实现(覆盖);他们还可以定义超出基本 Service 角色集的额外服务(扩展)。

让我们来看一个例子,以更好地定义什么是 Service。Hibernate 需要能够访问数据库的 JDBC Connections。它获取和释放这些 Connections 的方式是通过 ConnectionProvider 服务。该 Service 由接口(服务角色)org.hibernate.engine.jdbc.connections.spi.ConnectionProvider 定义,该接口声明了获取和释放 Connections 的方法。然后,该 Service 契约有多个实现,它们在管理 Connections 的方式上有所不同。

在内部,Hibernate 始终引用 org.hibernate.engine.jdbc.connections.spi.ConnectionProvider 而不是特定实现来使用 Service(我们将在讨论注册表时谈到生成 Service)。由于这个事实,其他 ConnectionProvider Service 实现可以轻松地插入。

这里没有什么革命性的东西;面向接口编程通常被认为是良好的编程实践。有趣的是 ServiceRegistry 和不同实现者的可插拔交换。

1.1.1. Service 契约

Service 的基本要求是实现标记接口 org.hibernate.service.Service。Hibernate 在内部使用它进行一些基本类型安全。

Service 还可以实现一些可选的生命周期相关契约

org.hibernate.service.spi.Startable

允许 Service 实现被通知它正在启动并即将投入使用。

org.hibernate.service.spi.Stoppable

允许 Service 实现被通知它正在停止并将从使用中移除。

org.hibernate.service.spi.ServiceRegistryAwareService

允许 Service 被注入对管理它的注册表的引用。有关更多详细信息,请参见Service 依赖项

org.hibernate.service.spi.Manageable

在启用 JMX 集成的情况下,将 Service 标记为可管理的 JMX。此功能仍不完整。

其他

不同的注册表实现也理解特定于该注册表的其他可选契约。有关详细信息,请参见什么是 ServiceRegistry中的每个注册表的详细信息。

1.1.2. Service 依赖

服务可以使用两种方法之一声明对其他服务的依赖关系。

@org.hibernate.service.spi.InjectService

Service 实现类上接受单个参数并使用 @InjectService 注释的任何方法都被认为是请求注入另一个服务。

默认情况下,方法参数的类型应为要注入的 Service 角色。如果参数类型与 Service 角色不同,则应使用 @InjectService 注释的 serviceRole 属性来显式命名角色。

默认情况下,注入的服务被认为是必需的,也就是说,如果缺少命名的依赖 Service,则启动将失败。如果要注入的 Service 是可选的,则应将 @InjectService 注释的 required 属性声明为 false(默认值为 true)。

org.hibernate.service.spi.ServiceRegistryAwareService

第二种方法是拉取方法,其中 Service 实现可选 Service 接口 org.hibernate.service.spi.ServiceRegistryAwareService,该接口声明一个 injectServices 方法。

在启动期间,Hibernate 会将 org.hibernate.service.ServiceRegistry 本身注入到实现此接口的服务中。然后,Service 可以使用 ServiceRegistry 引用来定位它需要的任何其他服务。

1.2. 什么是 ServiceRegistry

ServiceRegistry 在最基本的情况下,托管和管理服务。它的契约由 org.hibernate.service.ServiceRegistry 接口定义。

我们已经对服务进行了基本概述和定义。但服务还具有其他有趣的特性

  • 服务具有生命周期。

  • 它们具有作用域。

  • 服务可能依赖于其他服务。

  • 它们需要被生成(选择使用一个实现而不是另一个)。

ServiceRegistry 满足了所有这些需求。

简而言之,ServiceRegistry 充当控制反转 (IoC) 容器。

为什么不直接使用现有的 IoC 框架?主要原因是它必须尽可能轻量级和占用空间小。最初的设计也要求 Services 在运行时可交换,但由于基于代理的交换解决方案的性能问题,不幸的是必须删除该功能;计划是在以后调查实现可交换性的其他方法,以获得更好的性能。

ServiceServiceRegistry 相关联。ServiceRegistryService 设定作用域。ServiceRegistry 管理 Service 的生命周期。ServiceRegistry 处理将依赖关系注入 Service(实际上,支持拉取和推送/注入两种方法)。ServiceRegistries 也是分层的,这意味着 ServiceRegistry 可以具有父 ServiceRegistry。一个注册表中的服务可以依赖于并利用同一注册表以及任何父注册表中的服务。

1.3. 服务绑定

给定 Service 与给定 ServiceRegistry 的关联称为绑定,并由 org.hibernate.service.spi.ServiceBinding 接口表示。此外,ServiceBinding 与 ServiceRegistry 之间的特定契约由 org.hibernate.service.spi.ServiceBinding.ServiceLifecycleOwner 接口表示。

ServiceServiceRegistry 关联(绑定)有两种方式。

  • Service 可以直接实例化,然后传递给 ServiceRegistry

  • 可以将 ServiceInitiator 传递给 ServiceRegistry(如果需要 ServiceServiceRegistry 会使用它)

ServiceRegistry 实现通过调用重载的 org.hibernate.service.internal.AbstractServiceRegistryImpl#createServiceBinding 方法来注册绑定,该方法接受 Service 实例或 ServiceInitiator 实例。

每种特定类型的注册表都定义了自己的 ServiceInitiator 特化。

1.4. 服务注册表类型

目前,Hibernate 使用三种不同的 ServiceRegistry 实现来形成层次结构。每种类型都是为了类型安全而进行的专门化,但它们没有添加任何新功能。

1.4.1. BootstrapServiceRegistry

org.hibernate.boot.registry.BootstrapServiceRegistry 持有三个 Service,通常通过 org.hibernate.boot.registry.BootstrapServiceRegistryBuilder 工厂类构建。构建器提供对自定义这三个 Services 的类型安全访问。

此注册表持有 Hibernate 工作所需的大多数内容必须可用的服务。

在正常使用中,BootstrapServiceRegistry 没有父级。

BootstrapServiceRegistry 的服务不能扩展(添加到)也不能覆盖(替换)。

ClassLoaderService

ServiceService 角色是 org.hibernate.boot.registry.classloading.spi.ClassLoaderService。此 Service 定义了 Hibernate 与 ClassLoaders 交互的能力。Hibernate(或任何库)与 ClassLoaders 交互的方式根据托管应用程序的运行时环境而异。应用程序服务器、OSGi 容器和其他模块化类加载系统会强加非常具体的类加载要求。此 Service 为 Hibernate 提供了一种从这种环境复杂性中抽象出来的方式。同样重要的是,它以集中式、可交换的方式执行此操作。

Service 公开的一些特定功能包括

  • 按名称定位 Class 引用。这包括应用程序类以及集成类。

  • 定位资源(属性文件、xml 文件等)作为类路径资源

  • java.util.ServiceLoader 交互,Java 自己的 Service 提供程序发现机制

IntegratorService

ServiceService 角色是 org.hibernate.integrator.spi.IntegratorService. 应用程序、第三方集成商和其他所有都需要与 Hibernate 集成。在过去,这通常需要某些东西(通常是应用程序)来协调代表每个集成注册每个集成所需的片段。org.hibernate.integrator.spi.Integrator 契约将这种“集成 SPI”正式化。IntegratorService 管理所有已知集成器。

“集成器”的概念仍在积极定义和开发中。预计这些 SPI 会发生变化。

集成器以两种方式变得为人所知。

  • 可以通过调用 BootstrapServiceRegistryBuilder#with(Integrator) 手动注册集成器

  • 可以利用 ClassLoaderService 提供的标准 Java ServiceLoader 功能来发现集成器。Integrators 只需定义一个名为 /META-INF/services/org.hibernate.integrator.spi.Integrator 的文件并将其放在类路径上。ServiceLoader 详细介绍了此文件的格式,但本质上它按行列出实现 Integrator 的类的完全限定名称。

StrategySelector

ServiceService 角色是 org.hibernate.boot.registry.selector.spi.StrategySelector。可以将其视为简短命名服务。在过去,为了配置 Hibernate,用户通常需要给出对 Hibernate 内部类的完全限定名称引用。当然,当我们重构内部代码并将这些类移至不同的包结构时,这会导致很多问题。于是出现了简短命名的概念,使用策略/实现类的已定义且众所周知的简短名称

Service 中的简短名称映射可以进行管理,甚至可以由应用程序和集成商管理,这非常强大。有关此方面的更多信息,请参见

  • BootstrapServiceRegistryBuilder#applyStrategySelector

  • BootstrapServiceRegistryBuilder#applyStrategySelectors

  • org.hibernate.boot.registry.selector.StrategyRegistrationProvider 通过 ServiceLoader 发现

  • StrategySelector#registerStrategyImplementor / StrategySelector#unRegisterStrategyImplementor

1.4.2. StandardServiceRegistry

org.hibernate.boot.registry.StandardServiceRegistry 定义了主要的 Hibernate ServiceRegistry,它基于其父级 BootstrapServiceRegistry 构建。此注册表通常使用 org.hibernate.boot.registry.StandardServiceRegistryBuilder 类构建。默认情况下,它包含 Hibernate 使用的大多数 Service。有关 StandardServiceRegistry 中通常包含的 Service 的完整列表,请参见 org.hibernate.service.StandardServiceInitiators 的源代码。

在正常使用情况下,StandardServiceRegistry 的父级是 BootstrapServiceRegistry。

StandardServiceRegistry 的服务可以扩展(添加)和覆盖(替换)。

ConnectionProvider/MultiTenantConnectionProvider

提供 Hibernate 所需的 ConnectionsService。有两种截然不同的(互斥的)角色

org.hibernate.engine.jdbc.connections.spi.ConnectionProvider

在正常环境中提供 Connections

org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider

在多租户环境中提供(租户特定的)Connections

JdbcServices

org.hibernate.engine.jdbc.spi.JdbcServices 是一个聚合器 Service(一个聚合其他 Services 的 Service),它公开围绕 JDBC 可访问性的统一功能。

TransactionCoordinatorBuilder

org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder 由 Hibernate 用于与底层事务系统集成。它负责为每个 Hibernate Session 构建 org.hibernate.resource.transaction.spi.TransactionCoordinator 实例。

JtaPlatform

当使用基于 JTA 的 TransactionCoordinatorBuilder 时,org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform Service 为 Hibernate 提供对 JTA TransactionManagerUserTransaction 的访问,以及处理 Synchronization 注册。

JndiService

org.hibernate.engine.jndi.spi.JndiService Service 由 Hibernate 用于与 JNDI 上下文交互。Hibernate 的默认 JndiService 假设只有一个 InitialContext

RegionFactory

org.hibernate.cache.spi.RegionFactory Service 定义了与第三方缓存实现者作为二级缓存提供者的集成。

SessionFactoryServiceRegistryFactory

org.hibernate.service.spi.SessionFactoryServiceRegistryFactory 是一个 Service,它充当用于构建第三种类型 ServiceRegistry(SessionFactoryServiceRegistry)的工厂,我们将在后面讨论。我选择工厂作为服务的方式,是因为在当前设计中,对于何时需要构建 SessionFactoryServiceRegistry,确实没有一个好的公开挂钩点。

1.4.3. SessionFactoryServiceRegistry

org.hibernate.service.spi.SessionFactoryServiceRegistry 是第三个标准 Hibernate ServiceRegistrySessionFactoryServiceRegistry 用于保存需要访问 SessionFactoryService

通常,它的父注册表是 StandardServiceRegistry

在 4.x 中,集成器是在 SessionFactoryServiceRegistry 上操作的。

目前,SessionFactoryServiceRegistry 只包含四个 Service。

EventListenerRegistry

org.hibernate.event.service.spi.EventListenerRegistry 是在 SessionFactoryServiceRegistry 中管理的主要 Service。它是管理所有 Hibernate 事件侦听器的 ServiceIntegrators 的主要用例是更改侦听器注册表。

如果进行自定义侦听器注册,了解 org.hibernate.event.service.spi.DuplicationStrategy 及其对注册的影响非常重要。基本思想是告诉 Hibernate

  • 是什么使侦听器成为重复的

  • 如何处理重复注册(错误、先赢、后赢)

StatisticsImplementor

org.hibernate.stat.spi.StatisticsImplementor 是 Statistics API 的 SPI 部分;如果您愿意,也可以说是收集器部分。

NativeQueryInterpreter

org.hibernate.engine.query.spi.NativeQueryInterpreter 是 Hibernate 用于解释本机查询的 Service。它作为一个 Service 存在,主要是因为 OGM 等集成可以覆盖它。

CacheImplementor

org.hibernate.cache.spi.CacheImplementor 提供了一种方法来自定义 Hibernate 与二级缓存实现交互的方式。

1.5. 自定义服务

到目前为止,我们一直关注 Hibernate 提供的服务。但是,应用程序和集成也可以提供自己的服务,无论是

  • 提供标准 Service 的新实现(覆盖)

  • 提供全新的 Service 角色(扩展)

1.5.1. 自定义 Service 实现 (覆盖)

我们上面讨论了 Service 实现的可交换性。让我们看看一个实际示例。为了说明问题,假设我们开发了一个新的 ConnectionProvider,它与最新的连接池库集成。让我们看一下使此操作成为可能的步骤。

第一步是通过实现 ConnectionProvider 合同来开发实际的集成。

示例 1. 自定义 ConnectionProvider 实现
import java.lang.Override;

public class LatestAndGreatestConnectionProviderImpl
    implements ConnectionProvider, Startable, Stoppable, Configurable {

    private LatestAndGreatestPoolBuilder lagPoolBuilder;

    private LatestAndGreatestPool lagPool;

    private boolean available = false;

    @Override
    public void configure(Map configurationValues) {
        // extract our config from the settings map
        lagPoolBuilder = buildBuilder( configurationValues );
    }

    @Override
    public void start() {
        // start the underlying pool
        lagPool = lagPoolBuilder.buildPool();

        available = true;
    }

    @Override
    public void stop() {
        available = false;

        // stop the underlying pool
        lagPool.shutdown();
    }

    @Override
    public Connection getConnection() throws SQLException {
        if ( !available ) {
            throwException(
                "LatestAndGreatest ConnectionProvider not available for use" )
        }

        return lagPool.borrowConnection();
    }

    @Override
    public void closeConnection(Connection conn) throws SQLException {
        if ( !available ) {
            warn(
                "LatestAndGreatest ConnectionProvider not available for use" )
        }

        if ( conn == null ) {
            return;
        }

        lagPool.releaseConnection( conn );
    }

    ...
}

此时,我们需要决定如何将这个新的 ConnectionProvider 集成到 Hibernate 中。正如您可能猜到的那样,有多种方法。

作为第一个选项,我们可能只需要要求启动 StandardServiceRegistry 的代码进行集成。

示例 2. 通过 StandardServiceRegistryBuilder 覆盖服务实现
StandardServiceRegistryBuilder builder = ...;
...
builder.addService(
    ConnectionProvider.class,
    new LatestAndGreatestConnectionProviderImpl()
);
...

第二个选项是,如果我们的 LatestAndGreatestConnectionProviderImpl 应该始终使用,那么我们也可以提供一个 org.hibernate.service.spi.ServiceContributor 实现来代表用户处理集成。

示例 3. LatestAndGreatestConnectionProviderImplContributor
public class LatestAndGreatestConnectionProviderImplContributor1
        implements ServiceContributor {

    @Override
    public void contribute(StandardServiceRegistryBuilder serviceRegistryBuilder) {
        serviceRegistryBuilder.addService(
            ConnectionProvider.class,
            new LatestAndGreatestConnectionProviderImpl()
        );
    }
}

我们仍然需要能够告诉 Hibernate 为我们执行此集成。为此,我们利用 Java 的 ServiceLoader。在构建 StandardServiceRegistry 时,Hibernate 将查找类型为 org.hibernate.service.spi.ServiceContributor 的 JDK Service 提供者,并自动集成它们。我们在上面讨论过这种行为。在这里,我们将定义一个名为 META-INF/services/org.hibernate.service.spi.ServiceContributor 的类路径资源。此文件只有一行,命名我们的实现。

示例 4. META-INF/services/org.hibernate.service.spi.ServiceContributor
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1

第三个选项是,如果我们只想将我们的 LatestAndGreatestConnectionProviderImpl 作为配置选项提供,我们将再次使用 ServiceContributor,但方式略有不同。

示例 5. LatestAndGreatestConnectionProviderImplContributor 变体
public class LatestAndGreatestConnectionProviderImplContributor
    implements ServiceContributor {

    @Override
    public void contribute(
            standardserviceregistrybuilder serviceregistrybuilder) {

        // here we will register a short-name for our service strategy
        strategyselector selector = serviceregistrybuilder
            .getbootstrapserviceregistry().
            .getservice( strategyselector.class );

        selector.registerstrategyimplementor(
            connectionprovider.class,
            "lag"
            latestandgreatestconnectionproviderimpl.class
        );
    }
}

这样一来,应用程序就可以通过简短名称选择我们的 LatestAndGreatestConnectionProviderImpl

示例 6. 自定义服务简短名称
StandardServiceRegistryBuilder builder = ...;
...
builder.applySetting( "hibernate.connection.provider_class", "lag" );
...

1.5.2. 自定义 Service 角色 (扩展)

我们还可以让 ServiceRegistry 托管自定义服务(全新的 Service 角色)。例如,假设我们的应用程序将 Hibernate 事件发布到 JMS 主题,并且我们希望利用 Hibernate ServiceRegistry 来托管表示我们发布事件的 Service。因此,我们将扩展 ServiceRegistry 以托管此全新的 Service 角色,并管理其生命周期。

示例 7. EventPublishingService 服务角色
public interface EventPublishingService extends Service {

    public void publish(Event theEvent);
}
示例 8. EventPublishingService 实现
public class EventPublishingServiceImpl
    implements EventPublishingService, Configurable, Startable, Stoppable,
               ServiceRegistryAwareService {

    private ServiceRegistryImplementor serviceRegistry;

    private String jmsConnectionFactoryName;

    private String destinationName;

    private Connection jmsConnection;

    private Session jmsSession;

    private MessageProducer publisher;

    @Override
    public void injectServices(ServiceRegistryImplementor serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    public void configure(Map configurationValues) {
        this.jmsConnectionFactoryName = configurationValues
            .get( JMS_CONNECTION_FACTORY_NAME_SETTING );
        this.destinationName = configurationValues
            .get( JMS_DESTINATION_NAME_SETTING );
    }

    @Override
    public void start() {
        final JndiService jndiService = serviceRegistry
            .getService( JndiService.class );
        final ConnectionFactory jmsConnectionFactory = jndiService
            .locate( jmsConnectionFactoryName );

        this.jmsConnection = jmsConnectionFactory.createConnection();
        this.jmsSession = jmsConnection.createSession(
            true,
            Session.AUTO_ACKNOWLEDGE
        );

        final Destination destination = jndiService.locate( destinationName );

        this.publisher = jmsSession.createProducer( destination );
    }

    @Override
    public void publish(Event theEvent) {
        publisher.send( theEvent );
    }

    @Override
    public void stop() {
        publisher.close();
        jmsSession.close();
        jmsConnection.close();
    }
}
示例 9. 另一种 EventPublishingService 实现
public class DisabledEventPublishingServiceImpl implements EventPublishingService {

    public static DisabledEventPublishingServiceImpl INSTANCE =
        new DisabledEventPublishingServiceImpl();

    private DisabledEventPublishingServiceImpl() {
    }

    @Override
    public void publish(Event theEvent) {
        // nothing to do...
    }
}

因为我们有替代实现,所以开发一个启动器也是一个好主意,它可以在运行时在它们之间进行选择。

示例 10. EventPublishingServiceInitiator
public class EventPublishingServiceInitiator
    implements StandardServiceInitiator<EventPublishingService> {

    public static EventPublishingServiceInitiator INSTANCE =
            new EventPublishingServiceInitiator();

    public static final String ENABLE_PUBLISHING_SETTING =
            "com.acme.EventPublishingService.enabled";

    @Override
    public Class<R> getServiceInitiated() {
        return EventPublishingService.class;
    }

    @Override
    public R initiateService(
            Map configurationValues,
            ServiceRegistryImplementor registry) {

        final boolean enabled = extractBoolean(
                configurationValues,
                ENABLE_PUBLISHING_SETTING
        );
        if ( enabled ) {
            return new EventPublishingServiceImpl();
        }
        else {
            return DisabledEventPublishingServiceImpl.INSTANCE;
        }
    }

    ...
}

我们可以让应用程序将 EventPublishingServiceInitiator 注册到 StandardServiceRegistryBuilder,但编写一个 ServiceContributor 来为应用程序处理此操作要好得多。

示例 11. EventPublishingServiceContributor
public class EventPublishingServiceContributor
    implements ServiceContributor {

    @Override
    public void contribute(StandardServiceRegistryBuilder builder) {
        builder.addinitiator( eventpublishingserviceinitiator.instance );

        // if we wanted to allow other strategies (e.g. a jms
        // queue publisher) we might also register short names
        // here with the strategyselector.  the initiator would
        // then need to accept the strategy as a config setting
    }
}

2. 致谢

Hibernate ORM 的贡献者完整列表可以在 GitHub 仓库 中找到。

以下贡献者参与了本文档的编写

  • Steve Ebersole

  • Vlad Mihalcea