服务和注册表从 4.0 版开始作为一个正式的概念应运而生。但不同服务提供的功能实际上在 Hibernate 中已经存在了很长时间。新的功能是通过一个我们称为服务注册表的轻量级专用容器来管理它们、它们的声明周期和依赖关系。本指南旨在描述这些服务和注册表的设计和目的,并在适当情况下了解其实现的详细信息。它还将深入探讨第三方集成商和应用程序如何利用和自定义服务和注册表。
什么是服务?
服务以可插拔的方式提供各种类型功能。具体来说,它们是定义特定功能的接口,然后实现这些服务契约接口。接口称为服务角色;实现类称为服务实现。可插拔性源于这样的事实:服务实现遵守了服务角色接口定义的契约,而服务使用方针对服务角色进行编程,而不是针对实现进行编程。
所有服务都必须实现 org.hibernate.service.Service“标记”接口。Hibernate 在内部使用它来实现某些基本类型安全性;它不定义任何方法(目前)。 |
我们来看一个例子,以便更好地定义服务。Hibernate 需要能够访问与数据库的 JDBC 连接。它获取和释放这些连接的方式是通过 ConnectionProvider 服务。该服务由接口(服务角色)org.hibernate.engine.jdbc.connections.spi.ConnectionProvider 定义,该接口声明了获取和释放连接的方法。然后,针对该服务契约有多个实现,在如何实际管理连接方面有所不同
-
org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl 用于使用 1. javax.sql.DataSource
-
org.hibernate.c3p0.internal.C3P0ConnectionProvider用于使用 C3P0 连接池
-
等等。
在 Hibernate 内部,在使用服务时,总是引用 org.hibernate.engine.jdbc.connections.spi.ConnectionProvider,而不是特定实现(当我们谈到注册表时,我们稍后会涉及如何生成服务)。正因为如此,可以插入其他 ConnectionProvider 服务实现。
这里没有革命性的东西;针对接口进行编程通常被认为是良好的编程实践。有趣的是服务注册表和不同实现者的可插拔交换。
什么是服务注册表?
在最基本的层面上,服务注册表承载并管理服务。它的契约由 org.hibernate.service.ServiceRegistry 接口定义。
我们已经为服务提供基本概述和定义。但服务还具备其他一些有趣的特性。服务具有生命周期。它们具有作用域。服务可能依赖于其他服务。而且需要对它们进行生成(选择使用一个实现,而不是另一个)。ServiceRegistry 满足所有这些需求。
简单地说,ServiceRegistry 充当反转控制 (IoC) 容器。
尽管有一些近期的修正主义历史,但 Spring 并没有发明 IoC 或依赖项注入,甚至也不是第一个将它引入 Java 的。像 JBoss MicroContainer 和 Apache Avalon 等项目早在多年以前就早于 Spring,而且每个都使用了 IoC 和依赖项注入。ServiceRegistry 中的概念实际上与 Apache Avalon 非常类似。 |
为什么不直接使用现有的 IoC 框架?最主要的原因是,这必须尽可能轻量和尽可能小巧。初始设计也要求服务在运行时可调换,但不幸的是,由于基于代理的换用解决方法产生了性能问题,因此不得已将此功能移除(计划稍后以更高的性能研究实现可调换的其他方法)。
服务与 ServiceRegistry 关联。ServiceRegistry 设定服务的范围。ServiceRegistry 管理服务生命周期。ServiceRegistry 处理将依赖项注入到服务(实际上同时支持拉取和注入的推入方法)。ServiceRegistry 也是层次结构的,这意味着一个 ServiceRegistry 可以有父 ServiceRegistry。一个注册表中的服务可以依赖和使用同一个注册表中的服务以及任何父注册表中的服务。

服务绑定
特定服务与特定 ServiceRegistry 之间的关联称为绑定,并表示为 org.hibernate.service.spi.ServiceBinding 接口。此外,ServiceBinding 和 ServiceRegistry 之间的特定契约由 org.hibernate.service.spi.ServiceBinding.ServiceLifecycleOwner 接口表示。
有 2 种方法可以将服务与 ServiceRegistry 关联起来。服务可以得到直接实例化,然后再移交到 ServiceRegistry,或可以将 ServiceInitiator 交给 ServiceRegistry(ServiceRegistry 会在需要服务时使用它)。ServiceRegistry 实现(那些使用 org.hibernate.service.internal.AbstractServiceRegistryImpl 便利基本实现的实现)通过调用重载的 AbstractServiceRegistryImpl#createServiceBinding 方法来注册绑定,该方法接受 Service 实例或 ServiceInitiator 实例。但是,每种特定 ServiceRegistry 类型都具有专门的构建器,通常通过它来定义和自定义服务。
服务注册表的类型
目前 Hibernate 使用 3 个不同的 ServiceRegistry 实现形成一个层次结构。
BootstrapServiceRegistry
根 ServiceRegistry 是 org.hibernate.boot.registry.BootstrapServiceRegistry。BootstrapServiceRegistry 是 org.hibernate.service.ServiceRegistry 的一项专业化。BootstrapServiceRegistry 接口没有添加任何新行为,它仅仅是为了类型安全而进行的一项专业化。在正常使用中,BootstrapServiceRegistry 没有父级。
此注册表容纳对于 Hibernate 中的大多数部分的正常工作而言绝对必须可以利用的服务。 |
BootstrapServiceRegistry 通常持有 3 项服务,并且构建时通常使用 org.hibernate.boot.registry.BootstrapServiceRegistryBuilder 类。该构建器提供安全类型的访问权限,用于定制这 3 项服务。
ClassLoaderService
此项服务公开的是与其 ClassLoaders 进行交互的功能。Hibernate(或任何库)与 ClassLoaders 进行交互的方式根据托管应用程序的运行时环境而有所不同。应用程序服务器、OSGi 容器和其他模块化类加载系统制定了非常具体的类加载要求。此项服务为 Hibernate 从该环境复杂性中提供了抽象。同样重要的是,这是以集中且可互换的方式实现的。
在此项服务中公开的具体功能包括
-
按名称定位 java.lang.Class 引用。其中包括应用程序类和“集成”类。
-
定位资源(属性文件、xml 文件等)作为“类路径资源”
-
与 java.util.ServiceLoader 交互
此项服务的服务角色是 org.hibernate.boot.registry.classloading.spi.ClassLoaderService。
IntegratorService
应用程序、第三方集成器和其他人均需要与 Hibernate 集成。从历史上看,这需要某些内容(通常是应用程序)来协调注册每项集成代表每个集成所需的部分。org.hibernate.integrator.spi.Integrator 对此“集成 SPI”进行了形式化。IntegratorService 管理所有已知集成器。
在 5.0 代码库中,“集成器”的概念仍在以主动的方式进行定义和开发。期待这些 SPI 中会出现更改;事实上,这些更改已在包含 5.0 开发工具的存储库分支中开始。 |
集成器有 2 种已知方式。
-
集成器可以手动注册,通过调用 BootstrapServiceRegistryBuilder#with(Integrator)
-
集成器可以发现,利用 ClassLoaderService 提供的标准 Java java.util.ServiceLoader 功能。集成器只需定义一个名为 _/META-INF/services/org.hibernate.integrator.spi.Integrator_ 的文件并在类路径上提供它。java.util.ServiceLoader 详细涵盖了此文件的格式,但实质上,它按每行一个的方式列出实现了 org.hibernate.integrator.spi.Integrator 的全限定名称 (FQN) 类。
此项服务的服务角色是 org.hibernate.integrator.spi.IntegratorService。
StrategySelector
可将其视为“简短命名”服务。从历史上看,为了配置 Hibernate,用户经常需要向 Hibernate 内部类提供全限定名称 (FQN) 引用。
例如,为了告诉 Hibernate 使用基于 JDBC 的事务,我们需要告知它使用 org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory 类,方法是指定其全限定名称 (FQN) 作为配置的一部分
hibernate.transaction.factory_class=org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
当然在重构内部代码并将这些类移动到不同包结构时,会引发许多问题。因此,出现了简称的概念,即在 impl 类中使用定义明确且广为人知的“简称”。例如,这个 JdbcTransactionFactory 在简称“jdbc”下注册,因此
hibernate.transaction.factory_class=jdbc
在功能上等效于最初的示例。第二种形式不仅更简洁,还支持升级。
此服务中的简称映射可以由应用程序和集成商进行管理,效果非常强大。有关此方面的更多信息,请参见
-
BootstrapServiceRegistryBuilder#withStrategySelector
-
BootstrapServiceRegistryBuilder#withStrategySelectors
-
org.hibernate.boot.registry.selector.StrategyRegistrationProvider(通过 ServiceLoader 发现)
-
'StrategySelector#registerStrategyImplementor` / 'StrategySelector#unRegisterStrategyImplementor`
此服务的服务角色为 org.hibernate.boot.registry.selector.spi.StrategySelector。
StandardServiceRegistry
org.hibernate.boot.registry.StandardServiceRegistry 定义了 Hibernate 主 ServiceRegistry,构建在 BootstrapServiceRegistry 的基础上(BootstrapServiceRegistry 是其父项)。此注册表通常使用 org.hibernate.boot.registry.StandardServiceRegistryBuilder 类构建。默认情况下,它保留了 Hibernate 使用的大多数服务。有关通常存储在 StandardServiceRegistry 中的服务的完整列表,请参见 org.hibernate.service.StandardServiceInitiators 的源代码。一些值得注意的特定 StandardServiceRegistry 服务包括
ConnectionProvider/MultiTenantConnectionProvider
根据需要为 Hibernate 提供连接的服务。分为 2 个不同的(且互斥的)角色
-
org.hibernate.engine.jdbc.connections.spi.ConnectionProvider 在常规环境中提供连接。
-
org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider 在多租户环境中提供(特定于租户的)连接。
JdbcServices
org.hibernate.engine.jdbc.spi.JdbcServices 是一种聚合服务(一种聚合其他服务的服务),它围绕 JDBC 访问公开统一的功能。
TransactionFactory
org.hibernate.engine.transaction.spi.TransactionFactory 用于指示 Hibernate 如何控制或与事务集成。
JtaPlatform
使用基于 JTA 的 TransactionFactory 时,org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform 服务向 Hibernate 提供对 JTA TransactionManager 和 UserTransaction 的访问权,以及处理同步注册。
以下是 Hibernate 确定要使用的 JtaPlatform 的步骤(按优先级排序):
-
以“hibernate.transaction.jta.platform”为键的显式设置,该设置可以引用
-
JtaPlatform 实例
-
Class<? extends JtaPlatform> 引用
-
JtaPlatform 策略的名称(请参阅 StrategySelector 服务)
-
JtaPlatform 实现的 FQN
-
-
通过 org.hibernate.engine.transaction.jta.platform.spi.JtaPlatformResolver 服务进行发现,该服务默认
-
通过 ServiceLoader 查找 org.hibernate.engine.transaction.jta.platform.spi.JtaPlatformProvider 实现,如果找到实现,则会使用其报告的 JtaPlatform(先到先得)。
-
尝试对各种环境执行许多众所周知的类查找。
-
RegionFactory
就启动底层缓存提供程序而言,这是一个二级缓存服务
SessionFactoryServiceRegistryFactory
org.hibernate.service.spi.SessionFactoryServiceRegistryFactory 是一项服务,它作为以下第三种服务注册表的工厂,我们将在后面讨论SessionFactoryServiceRegistry。我选择“以服务作为工厂”的方式,因为在当前设计中,当需要构建SessionFactoryServiceRegistry 时,实际上并没有一个适合插入的公开挂钩点。这在 5.0 中可能会改变
SessionFactoryServiceRegistry
org.hibernate.service.spi.SessionFactoryServiceRegistry 是第三种标准 Hibernate 服务注册表。通常,它的父注册表是 StandardServiceRegistry。SessionFactoryServiceRegistry 旨在保存需要访问 SessionFactory 的服务。目前只包括 3 项服务。
在 4.x 中,集成器在 SessionFactoryServiceRegistry 中运行…… |
EventListenerRegistry
org.hibernate.event.service.spi.EventListenerRegistry 是在 SessionFactoryServiceRegistry 中管理的一项重要服务。这是管理和公开所有 Hibernate 事件监听器的一项服务。集成器的一个主要用例是更改监听器注册表。
如果执行自定义监听器注册,则务必理解 org.hibernate.event.service.spi.DuplicationStrategy 及其对注册的影响。基本思路是告知 Hibernate
-
如何让监听器成为重复监听器
-
如何处理重复注册(出错、先胜、后胜)
StatisticsImplementor
org.hibernate.stat.spi.StatisticsImplementor 是 org.hibernate.stat.Statistics API 的 SPI 部分。如果您愿意,即为收集器部分。
服务生命周期
管理服务的生命周期作为这些服务的容器,是服务注册表的主要作用。服务总的生命周期是
初始化(创建)
需要初始化/创建服务。当我们讨论构建服务注册表时,我们将详细探讨更多内容。但一般来说
-
可以直接实例化服务并将其交给服务注册表
-
可以将
ServiceInitiator
交给服务注册表,以便按需初始化服务。
配置
服务可以选择实现 org.hibernate.service.spi.Configurable
接口,以在最初引导期间将 java.util.Map
的配置设置交给 Hibernate。在初始化后但在使用前调用 Configurable#configure
启动
服务可以选择实现 org.hibernate.service.spi.Startable
以便在投入“使用中”之前收到回调。从反射的角度来说,对于需要 Startable
的服务,通常也需要 Stoppable
(停止)也是一种好习惯。
停止
服务可以选择实现 org.hibernate.service.spi.Stoppable
以便在将服务从“使用中”的状态中取出时收到回调,作为服务注册表关闭的一部分。
可管理(JMX)
服务可以选择实现 org.hibernate.service.spi.Manageable
,以便在 JMX 中可用。
此特点仍在设计/开发 |
服务依赖关系
服务有时依赖于其他服务。例如,DataSourceConnectionProvider 服务实现通常需要访问 JndiService 来执行 JNDI 查找。这具有 2 个意义。首先,它表示 DataSourceConnectionProvider 需要访问 JndiService。其次,它表示 JndiService 在 DataSourceConnectionProvider 中使用之前必须充分“使用”。
获取对依赖服务访问权限的方法有 2 种
-
使服务实现
org.hibernate.service.spi.ServiceRegistryAwareService
,它会将 ServiceRegistry 注入到服务。你可以查看需要访问的任何服务。查找的返回服务将完全可以供你使用。 -
使用
@org.hibernate.service.spi.InjectService
注入特定服务。-
通常推断出需要注入的服务角色由方法的参数类型决定,该方法附加这个注解。如果参数类型不同于服务角色,则使用
InjectService#serviceRole
显式命名角色。 -
默认情况下,需要注入的服务被认为是必需的(如果找不到服务,将抛出异常)。如果需要注入的服务是可选的,请使用
InjectService#required=false
。
-
构建 ServiceRegistry
构建完成后,ServiceRegistry 通常被视为不可变的。服务本身可能接受重新配置,但是此处的不变性表示添加/替换服务。因此,承载在特定 ServiceRegistry 中的所有服务都必须预先知道。为此,构建 ServiceRegistry 通常会采用 构建器。
构建 BootstrapServiceRegistry
构建 BootstrapServiceRegistry
通常通过“org.hibernate.boot.registry.BootstrapServiceRegistryBuilder”类完成,该类公开用于定义要使用的 ClassLoader
,要合并的不可发现的 Integrator
等等的方法。
默认情况下,Hibernate 将使用线程上下文类加载器(如果存在),以及其自己的类类加载器作为它在要求加载类或资源或执行 ServiceLoader 解析时将咨询的 ClassLoader。你可以通过重载 BootstrapServiceRegistryBuilder#with(ClassLoader)
方法告诉 Hibernate 考虑任何其他 ClassLoaders
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
.with( anAdditionalClassLoader )
.with( anotherAdditionalClassLoader )
.build();
你还可以使用 BootstrapServiceRegistryBuilder#with(ClassLoaderService) 告诉 Hibernate 使用完全不同的 ClassLoaderService 实现。 |
Integrator 通常通过 JDK ServiceLoader
机制发现。要告诉 Hibernate 不会发现的 Integrator(无论什么原因),请使用 BootstrapServiceRegistryBuilder#with(Integrator)
方法
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
.with( new MyCustomIntegrator() )
.build();
BootstrapServiceRegistryBuilder
还公开了方法,用于添加额外的策略选择。假设我们开发了一个名为 CORBATransactionFactory 的基于 CORBA 的自定义 TransactionFactory,并且我们想通过简称使其可用。一种方法是在构建 BootstrapServiceRegistry 期间显式设置简称
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
.withStrategySelector( TransactionFactory.class, "corba", CORBATransactionFactory.class )
.build();
如果要分发 CORBATransactionFactory,则可以开发 org.hibernate.boot.registry.selector.StrategyRegistrationProvider
public class CORBATransactionFactoryStrategyRegistrationProvider implements StrategyRegistrationProvider {
public Iterable<StrategyRegistration> getStrategyRegistrations() {
return Collections.singletonList(
(StrategyRegistration) new SimpleStrategyRegistrationImpl<TransactionFactory>(
TransactionFactory.class,
CORBATransactionFactory.class,
"corba"
)
);
}
}
我们可以显式注册该提供程序
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
.withStrategySelectors( new CORBATransactionFactoryStrategyRegistrationProvider() )
.build();
或通过将 META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider
文件添加到我们的构件命名 CORBATransactionFactoryStrategyRegistrationProvider
来定义发现。
我们可能一次组合多个这些。
BootstrapServiceRegistry bootstrapServiceRegistry = new BootstrapServiceRegistryBuilder()
.with( anAdditionalClassLoader )
.with( anotherAdditionalClassLoader )
.with( new MyCustomIntegrator() )
.withStrategySelector( ConnectionProvider.class, "custom", MyCustomConnectionProvider.class )
.withStrategySelectors( new CORBATransactionFactoryStrategyRegistrationProvider() )
.build();
构建 StandardServiceRegistry
构建 StandardServiceRegistry
通常通过暴露用于管理设置和控制由构建的 StandardServiceRegistry 托管的服务的方法的“org.hibernate.boot.registry.StandardServiceRegistryBuilder`来完成。
管理设置可以像直接地告诉生成器一个或更多设置一样简单
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySetting( "hibernate.hbm2ddl.auto", true )
.applySettings( Collections.singletonMap( "hibernate.transaction.factory_class", "jdbc" ) )
.build();
或者我们可以告诉它从各种文件中加载设置
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.configure() (1)
.configure( "com/acme/hibernate.cfg.xml" ) (2)
.loadProperties( "com/acme/hibernate.properties" ) (3)
.build();
1 | 通过 ClassLoader 资源查找 hibernate.cfg.xml 从 XML 文件(符合 Hibernate cfg.xml DTD)中加载设置 |
2 | 通过 ClassLoader 资源查找 com/acme/hibernate.cfg.xml 从 XML 文件(符合 Hibernate cfg.xml DTD)中加载设置 |
3 | 通过 ClassLoader 资源查找 com/acme/hibernate.properties 从 Properties 中加载设置 |
StandardServiceRegistryBuilder
上的其他感兴趣的方法与自定义要使用的服务相关。我们可以传递要使用的服务实例或前面已讨论要使用的 ServiceInitiator。有 2 种不同的方式自定义要使用的服务
构建 StandardServiceRegistry - 重写
这里的目的是重写或替换服务实现。许多标准 ServiceInitiator 在设置中查找以确定要使用的适当服务。回到一个我们使用多次的示例
hibernate.transaction.factory_class=jdbc
标准 TransactionFactoryInitiator
寻找此设置并确定要使用哪个 TransactionFactory
服务实现。让我们假设由于某种原因我们总是希望它使用 JdbcTransactionFactory
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.addService( TransactionFactory.class, new JdbcTransactionFactory() )
.build();
或者,我们想要以不同的方式确定要使用的服务实现
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.addInitiator( new MyCustomTransactionFactoryInitiator() )
.build();
构建 StandardServiceRegistry - 扩展
这里的目的是让 ServiceRegistry 托管自定义服务(完全新的服务角色)。举个例子,假设我们的应用程序将 Hibernate 事件发布到 JMS 主题,并且我们希望利用 Hibernate ServiceRegistry 托管代表我们的 TopicPublisher 的服务。因此,我们将扩展 ServiceRegistry 以托管这个全新的服务角色
/**
* The service role
*/
public interface EventPublishingService extends Service {
public void publish(Event theEvent);
}
/**
* A disabled (no-op) impl
*/
public class DisabledEventPublishingServiceImpl implements EventPublishingService {
public static DisabledEventPublishingServiceImpl INSTANCE = new DisabledEventPublishingServiceImpl();
private DisabledEventPublishingServiceImpl() {
}
@Override
public void publish(Event theEvent) {
// nothing to do...
}
}
/**
* A standard impl
*/
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();
}
}
public class EventPublishingServiceInitiator implements StandardServiceInitiator<EventPublishingService> {
public static EventPublishingServiceInitiator INSTANCE = new EventPublishingServiceInitiator();
public static final String ENABLE_PUBLISHING_SETTING = "com.acme.EventPublishingService.enabled";
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;
}
}
}
现在,让我们告诉 Hibernate 此自定义服务
StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.addInitiator( EventPublishingServiceInitiator.INSTANCE )
...
.build();
结论
TODO