前言
Hibernate 提供许多集成点,允许集成新功能或为标准功能提供自定义行为。本指南涵盖了这些集成点,并面向将
-
将 Hibernate 集成到 Java EE 应用程序服务器、Spring 框架、缓存解决方案(例如 Infinispan、Ehcache、Hazelcast)中的软件开发人员和架构师。
-
提供自定义集成
-
希望覆盖标准功能
1. 服务和注册表
服务和注册表从 4.0 开始作为正式概念出现。但不同服务提供的功能实际上在 Hibernate 中存在了很久。新的变化是通过一个轻量级的专用容器(我们称之为 ServiceRegistry
)来管理它们、它们的生存周期和依赖关系。本指南的目的是描述这些 Services
和 Registries
的设计和目的,并在适当的情况下查看其实现细节。它还将深入探讨第三方集成商和应用程序如何利用和自定义 Services
和 Registries
。
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
在运行时可交换,但由于基于代理的交换解决方案的性能问题,不幸的是必须删除该功能;计划是在以后调查实现可交换性的其他方法,以获得更好的性能。
Service
与 ServiceRegistry
相关联。ServiceRegistry
为 Service
设定作用域。ServiceRegistry
管理 Service
的生命周期。ServiceRegistry
处理将依赖关系注入 Service
(实际上,支持拉取和推送/注入两种方法)。ServiceRegistries
也是分层的,这意味着 ServiceRegistry
可以具有父 ServiceRegistry
。一个注册表中的服务可以依赖于并利用同一注册表以及任何父注册表中的服务。
1.3. 服务绑定
给定 Service
与给定 ServiceRegistry
的关联称为绑定,并由 org.hibernate.service.spi.ServiceBinding
接口表示。此外,ServiceBinding 与 ServiceRegistry
之间的特定契约由 org.hibernate.service.spi.ServiceBinding.ServiceLifecycleOwner
接口表示。
Service
与 ServiceRegistry
关联(绑定)有两种方式。
-
Service
可以直接实例化,然后传递给ServiceRegistry
-
可以将
ServiceInitiator
传递给ServiceRegistry
(如果需要Service
,ServiceRegistry
会使用它)
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
此 Service
的 Service
角色是 org.hibernate.boot.registry.classloading.spi.ClassLoaderService
。此 Service
定义了 Hibernate 与 ClassLoaders
交互的能力。Hibernate(或任何库)与 ClassLoaders
交互的方式根据托管应用程序的运行时环境而异。应用程序服务器、OSGi 容器和其他模块化类加载系统会强加非常具体的类加载要求。此 Service
为 Hibernate 提供了一种从这种环境复杂性中抽象出来的方式。同样重要的是,它以集中式、可交换的方式执行此操作。
此 Service
公开的一些特定功能包括
-
按名称定位
Class
引用。这包括应用程序类以及集成类。 -
定位资源(属性文件、xml 文件等)作为类路径资源
-
与
java.util.ServiceLoader
交互,Java 自己的Service
提供程序发现机制
IntegratorService
此 Service
的 Service
角色是 org.hibernate.integrator.spi.IntegratorService.
应用程序、第三方集成商和其他所有都需要与 Hibernate 集成。在过去,这通常需要某些东西(通常是应用程序)来协调代表每个集成注册每个集成所需的片段。org.hibernate.integrator.spi.Integrator
契约将这种“集成 SPI”正式化。IntegratorService 管理所有已知集成器。
“集成器”的概念仍在积极定义和开发中。预计这些 SPI 会发生变化。 |
集成器以两种方式变得为人所知。
-
可以通过调用
BootstrapServiceRegistryBuilder#with(Integrator)
手动注册集成器 -
可以利用
ClassLoaderService
提供的标准 JavaServiceLoader
功能来发现集成器。Integrators
只需定义一个名为/META-INF/services/org.hibernate.integrator.spi.Integrator
的文件并将其放在类路径上。ServiceLoader
详细介绍了此文件的格式,但本质上它按行列出实现Integrator
的类的完全限定名称。
StrategySelector
此 Service
的 Service
角色是 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 所需的 Connections
的 Service
。有两种截然不同的(互斥的)角色
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 TransactionManager
和 UserTransaction
的访问,以及处理 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 ServiceRegistry
。SessionFactoryServiceRegistry
用于保存需要访问 SessionFactory
的 Service
。
通常,它的父注册表是 StandardServiceRegistry
。
在 4.x 中,集成器是在 |
目前,SessionFactoryServiceRegistry
只包含四个 Service。
EventListenerRegistry
org.hibernate.event.service.spi.EventListenerRegistry
是在 SessionFactoryServiceRegistry
中管理的主要 Service
。它是管理所有 Hibernate 事件侦听器的 Service
。Integrators
的主要用例是更改侦听器注册表。
如果进行自定义侦听器注册,了解 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
合同来开发实际的集成。
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
的代码进行集成。
StandardServiceRegistryBuilder
覆盖服务实现StandardServiceRegistryBuilder builder = ...;
...
builder.addService(
ConnectionProvider.class,
new LatestAndGreatestConnectionProviderImpl()
);
...
第二个选项是,如果我们的 LatestAndGreatestConnectionProviderImpl
应该始终使用,那么我们也可以提供一个 org.hibernate.service.spi.ServiceContributor
实现来代表用户处理集成。
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
的类路径资源。此文件只有一行,命名我们的实现。
META-INF/services/org.hibernate.service.spi.ServiceContributor
fully.qualified.package.LatestAndGreatestConnectionProviderImplContributor1
第三个选项是,如果我们只想将我们的 LatestAndGreatestConnectionProviderImpl
作为配置选项提供,我们将再次使用 ServiceContributor
,但方式略有不同。
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
。
StandardServiceRegistryBuilder builder = ...;
...
builder.applySetting( "hibernate.connection.provider_class", "lag" );
...
1.5.2. 自定义 Service
角色 (扩展)
我们还可以让 ServiceRegistry
托管自定义服务(全新的 Service
角色)。例如,假设我们的应用程序将 Hibernate 事件发布到 JMS 主题,并且我们希望利用 Hibernate ServiceRegistry
来托管表示我们发布事件的 Service
。因此,我们将扩展 ServiceRegistry
以托管此全新的 Service
角色,并管理其生命周期。
EventPublishingService
服务角色public interface EventPublishingService extends Service {
public void publish(Event theEvent);
}
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();
}
}
EventPublishingService
实现public class DisabledEventPublishingServiceImpl implements EventPublishingService {
public static DisabledEventPublishingServiceImpl INSTANCE =
new DisabledEventPublishingServiceImpl();
private DisabledEventPublishingServiceImpl() {
}
@Override
public void publish(Event theEvent) {
// nothing to do...
}
}
因为我们有替代实现,所以开发一个启动器也是一个好主意,它可以在运行时在它们之间进行选择。
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
来为应用程序处理此操作要好得多。
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
}
}