Spring FactoryBean 初始化问题
前提
项目使用了 EBean 框架作为数据库依赖管理,通过 FactoryBean 接口,实现了 Ebean Server 的配置初始化操作。
@Component
@Qualifier("defaultDatabase")
@Primary
class EbeanDatabaseFactory(
private val primaryDataSource: DataSource
) : FactoryBean<Database> {
override fun getObject(): Database? =
DatabaseConfig()
.apply {
dataSource = primaryDataSource
isDdlRun = false
isDdlGenerate = false
isDefaultServer = true
namingConvention = UnderscoreNamingConvention()
name = "defaultDatabase"
}
.let(DatabaseFactory::create)
override fun getObjectType(): Class<*>? = Database::class.java
override fun isSingleton(): Boolean = true
问题描述
在 ORM 层,通过获取 Database
对象做数据库操作
fun poloDatabase(): Database {
return DB.byName("defaultDatabase")
}
出现报错:
Caused by: io.ebean.datasource.DataSourceConfigurationException: Configuration error creating DataSource for the default Database. This typically means a missing application-test.yaml or missing ebean-test-config dependency. See https://ebean.io/docs/trouble-shooting#datasource
at io.ebean.Ebean$ServerManager.<init>(Ebean.java:87)
at io.ebean.Ebean$ServerManager.<init>(Ebean.java:50)
at io.ebean.Ebean.<clinit>(Ebean.java:45)
... 70 common frames omitted
Caused by: io.ebean.datasource.DataSourceConfigurationException: DataSource user is null?
at io.ebean.datasource.pool.ConnectionPool.<init>(ConnectionPool.java:220)
at io.ebean.datasource.core.Factory.createPool(Factory.java:15)
at io.ebeaninternal.server.core.DefaultContainer.getDataSourceFromConfig(DefaultContainer.java:290)
at io.ebeaninternal.server.core.DefaultContainer.setDataSource(DefaultContainer.java:234)
at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:100)
at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:70)
at io.ebeaninternal.server.core.DefaultContainer.createServer(DefaultContainer.java:36)
at io.ebean.EbeanServerFactory.create(EbeanServerFactory.java:58)
at io.ebean.Ebean$ServerManager.getWithCreate(Ebean.java:128)
at io.ebean.Ebean$ServerManager.<init>(Ebean.java:77)
... 72 common frames omitted
问题排查
在 FactoryBean.getObject
打断点排查没有执行到该方法,既 数据库没有初始化。
怀疑是生命周期问题,导致没有注入依赖。
查找官方 spring 文档,发现以下说明
A standard FactoryBean is not expected to initialize eagerly: Its
FactoryBean.getObject()
will only be called for actual access, even in case of a singleton object.
即 FactoryBean.getObject 需要显式调用。
为何之前没有这个问题
之前在代码中显式要求依赖注入了
@Component
class UserArgumentResolver(
@Qualifier("defaultDatabase") private var poloDatabase: Database
): HandlerMethodArgumentResolver {
问题解决
显式调用,或者显式依赖注入,在这里感觉有点多余。
在 FactoryBean
下有个子接口,SmartFactoryBean
实现了 eagerInit 。
因此,数据库 Bean 改为实现该接口。
@Component
@Qualifier("defaultDatabase")
@Primary
class EbeanDatabaseFactory(
private val primaryDataSource: DataSource
) : SmartFactoryBean<Database> {
override fun getObject(): Database? =
DatabaseConfig()
.apply {
dataSource = primaryDataSource
isDdlRun = false
isDdlGenerate = false
isDefaultServer = true
namingConvention = UnderscoreNamingConvention()
name = "defaultDatabase"
}
.let(DatabaseFactory::create)
override fun getObjectType(): Class<*>? = Database::class.java
override fun isSingleton(): Boolean = true
override fun isPrototype(): Boolean = true
override fun isEagerInit(): Boolean = true
}