You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Play Framework+Guice环境下让类先于HikariCP加载初始化

问题描述

我需要确保应用中的单例服务类MyClassThatNeedToBeLoadedBeforeHikariCP在HikariCP初始化并建立数据库连接前完成加载。Play Framework默认会自动初始化HikariCP,我不知道该如何实现这一加载顺序。

我的类代码如下:

@Singleton
class MyClassThatNeedToBeLoadedBeforeHikariCP() {

  // 初始化OpenTelemetry全局状态

}

如果是让该类先于自定义类加载,我可以用常规的依赖注入方式:

@Singleton
class OtherClassToLoadAfterMyClass() @Inject()(myClass: MyClassThatNeedToBeLoadedBeforeHikariCP) {
}

但现在需要让它先于HikariCP加载,请问如何在Guice中定义HikariCP与自定义类的依赖关系?

补充背景:我使用的Trino JDBC驱动也会设置全局状态,如果它先于MyClassThatNeedToBeLoadedBeforeHikariCP初始化,我的类会初始化失败。这是因为OpenTelemetry的全局状态只能被定义一次,出现该问题时的堆栈信息如下:

Caused by: java.lang.IllegalStateException: GlobalOpenTelemetry.set has already been called. GlobalOpenTelemetry.set must be called only once before any calls to GlobalOpenTelemetry.get. If you are using the OpenTelemetrySdk, use OpenTelemetrySdkBuilder.buildAndRegisterGlobal instead. Previous invocation set to cause of this exception.
         at io.opentelemetry.api.GlobalOpenTelemetry.set(GlobalOpenTelemetry.java:104)
         at io.opentelemetry.sdk.OpenTelemetrySdkBuilder.buildAndRegisterGlobal(OpenTelemetrySdkBuilder.java:85)
         at com.myapp.metrics.setup.OTELService.<init>(OTELService.scala:41)
         ^^^^^^^^^ == MyClassThatNeedToBeLoadedBeforeHikariCP
         at com.myapp.metrics.setup.OTELService$$FastClassByGuice$$1270092a.newInstance(<generated>)
         at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:89)
...
Caused by: java.lang.Throwable
         at io.opentelemetry.api.GlobalOpenTelemetry.set(GlobalOpenTelemetry.java:112)
         at io.opentelemetry.api.GlobalOpenTelemetry.get(GlobalOpenTelemetry.java:82)
         at io.trino.jdbc.NonRegisteringTrinoDriver.instrumentClient(NonRegisteringTrinoDriver.java:68)
         at io.trino.jdbc.NonRegisteringTrinoDriver.connect(NonRegisteringTrinoDriver.java:62)
         at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
         at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358)
解决方案

要让你的单例类先于HikariCP加载,核心是在Guice中强制HikariCP相关的依赖组件依赖你的自定义类,这样Guice的依赖解析器会优先初始化你的类。具体可以通过以下两种方式实现:

方式一:自定义Guice模块,为HikariCP核心组件添加依赖

Play Framework的HikariCP集成基于HikariCPModule实现,你可以创建自定义模块,修改HikariCP数据源的绑定逻辑,让它依赖你的MyClassThatNeedToBeLoadedBeforeHikariCP:

import com.google.inject.AbstractModule
import com.zaxxer.hikari.HikariDataSource
import play.api.db.HikariCPModule

class PreloadOTELModule extends AbstractModule {
  override def configure(): Unit = {
    // 将你的类绑定为饿汉单例,确保启动时立即初始化
    bind(classOf[MyClassThatNeedToBeLoadedBeforeHikariCP])
      .asEagerSingleton()

    // 修改HikariDataSource绑定,强制它依赖你的类
    bind(classOf[HikariDataSource])
      .toProvider(classOf[HikariCPModule.HikariDataSourceProvider])
      .asEagerSingleton()
      .addDependency(classOf[MyClassThatNeedToBeLoadedBeforeHikariCP])
  }
}

然后在application.conf中注册该模块,确保它在Play默认的HikariCP模块之前加载:

play.modules.enabled += "com.yourpackage.PreloadOTELModule"

方式二:自定义Play数据库组件

如果方式一不生效,可以通过自定义Play的DBComponents,在创建数据源前确保你的类已初始化:

import play.api.db.{DBComponents, HikariCPComponents}
import play.api.inject.ApplicationLifecycle
import play.api.{Configuration, Environment}

class CustomDBComponents(
  environment: Environment,
  configuration: Configuration,
  lifecycle: ApplicationLifecycle,
  myClass: MyClassThatNeedToBeLoadedBeforeHikariCP // 注入你的类,强制优先初始化
) extends DBComponents with HikariCPComponents {
  override protected def env: Environment = environment
  override protected def config: Configuration = configuration
  override protected def applicationLifecycle: ApplicationLifecycle = lifecycle
}

接着在自定义模块中绑定这个组件:

import com.google.inject.AbstractModule
import play.api.db.DBComponents

class CustomDBModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[DBComponents])
      .to(classOf[CustomDBComponents])
      .asEagerSingleton()
  }
}

同样在application.conf中注册该模块。

关键注意点

  • 确保你的类标记为@Singleton且通过asEagerSingleton()绑定,避免懒加载导致初始化时机滞后。
  • 核心逻辑是让HikariCP的所有初始化入口都依赖你的类,Guice的依赖图会自动保证依赖项优先实例化。

内容的提问来源于stack exchange,提问作者Gaël J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:40:30