spring 引导启动过程失败,lateinit var 未初始化:DI 问题

spring boot startup process failed with lateinit var not initialized : DI Issue

我正在构建一个后端,它将从几个外部 API 获取一些数据,在一些进程之后填充数据库,然后通过 rest api 公开这些数据。在 springboot 中使用 JPA 和 postgresql。

我已经创建了实体、存储库并从网络客户端获取外部 api。 但是,我有一个依赖注入问题。我阅读了大量文章和问题,但无法使其正常工作。尝试注入存储库时,我遇到了众所周知的错误 lateinit var not initialized。 我也试过构造函数注入,但还是不行。似乎存储库不被认为是可以自动装配的 bean。

如有任何帮助,我们将不胜感激

已修复 请参阅下面的解决方案。 PB 在职

申请。 运行 来自上下文的启动进程

@SpringBootApplication(exclude = [JacksonAutoConfiguration::class])
class SpringbootkotlinApplication
   fun main(args: Array<String>) {
      val context = runApplication<SpringbootkotlinApplication>(*args)
      val startupProcess = context.getBean(StartupProcesses::class.java)
      startupProcess.fetchGroup()
   }

startup class as @component :fetches external api 并调用服务将数据保存在 db

@Component
class StartupProcesses  {
        fun fetchGroup() {
            val fetch = IronMaidenSourceApi.fetchIronMaidenSource() //<-- fetch ext api OK
            SplitDataSourceToEntities().populateGroup(fetch) //<-- call service to populate db
    }
}

应该通过存储库填充数据库但在 lateinit 存储库中出错的服务

@Service
class SplitDataSourceToEntities  {
        @Autowired
        lateinit var repo:IronMaidenGroupRepository // <-- error is here

        fun populateGroup(dto: DTOIronMaidenAPi): IronMaidenGroupEntity {
            val dbgroup = IronMaidenGroupEntity(
                groupId = dto.id!!,
                name = dto.name ?: ""
            )
            return repo.save(dbgroup)
        }
}

repo,扩展 JPA 存储库

import com.jerome.springbootkotlin.model.dbentities.ironmaidengroupentity.IronMaidenGroupEntity
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface IronMaidenGroupRepository:JpaRepository<IronMaidenGroupEntity, String> {
}

解决方案

服务应该这样定义(SplitDataSourceToEntitiesclass 也应该late init 而不是被实例化)

@Component
class StartupProcesses  {
        @Autowired
        lateinit var splitDataSourceToEntities: SplitDataSourceToEntities // <--add this lateinit
        fun fetchGroup() {
            val fetch = IronMaidenSourceApi.fetchIronMaidenSource()
            splitDataSourceToEntities.populateGroup(fetch) //<-- fix is here
    }
}

@Autowired 仅在由 Spring

管理时有效

值得注意的是,@Autowired 仅在 class 已被 Spring 初始化时才有效。在您的代码中,您在 class SplitDataSourceToEntities 中有一个 @Autowired 注释,但是您在 StartupProcesses.fetchGroup 中手动实例化了 SplitDataSourceToEntities。那行不通,因为 Spring 不可能 auto-wire lateinit var.

使用您的服务的自动装配实例可以轻松解决该问题:

@Component
class StartupProcesses  {
    @Autowired
    lateinit var splitDataSourceToEntities: SplitDataSourceToEntities
    
    fun fetchGroup() {
        val fetch = IronMaidenSourceApi.fetchIronMaidenSource() //<-- fetch ext api OK
        splitDataSourceToEntities.populateGroup(fetch) //<-- call service to populate db
    }
}

帮助Spring找到你的JpaRepository

此外,您可能需要向您的应用程序添加一个 @EnableJpaRepositories 注释:

@EnableJpaRepositories(basePackageClasses = [IronMaidenGroupRepository::class])
@SpringBootApplication(exclude = [JacksonAutoConfiguration::class])
class SpringbootkotlinApplication
...

您可以直接通过名称定义包,而不是 basePackageClasses = ...,但是您的示例中不包含包名。


你必须使用@Autowired吗?

正在考虑您在评论中提出的问题:

你的设计没有错,因为@Component@Autowired不是没有必要去“玩”。您也可以去掉 @Autowired 注释并将这些变量定义为构造函数参数等。 可能看起来像这样:

class SpringbootkotlinApplication
fun main(args: Array<String>) {
    val context = runApplication<SpringbootkotlinApplication>(*args)
    val repo = context.getBean(IronMaidenGroupRepository::class.java)
    StartupProcesses(SplitDataSourceToEntities(repo))
}

@Component
class StartupProcesses(
    val splitDataSourceToEntities: SplitDataSourceToEntities
) {
    ...
}


@Service
class SplitDataSourceToEntities(
    val repo: IronMaidenGroupRepository
) {
    ...
}

现在只有 Repository 由 Spring 管理,但在 flip-side 上,您必须自己管理其他所有内容,当您的 class 时,这可能会变得非常乏味es 和它们的依赖性增长。只让 Spring 管理所有依赖关系会更舒服(并最终导致更好的可读代码)。