Spring Boot 和 Kotlin DSL 配置

问题描述 投票:0回答:3

最近,我被分配到一个项目,该项目禁用一些自动配置并主要使用 KotlinDSL 手动配置 Spring Boot 应用程序。

@SpringBootApplication(exclude = {
        DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        JpaRepositoriesAutoConfiguration.class,
        HibernateJpaAutoConfiguration.class,
        CassandraDataAutoConfiguration.class,
        CassandraAutoConfiguration.class
})

我面临着我相信 Kotlin lang 与 Spring 集成的问题。

让我向您展示设置。

  1. 其中包含 @Transactional 方法的抽象登录策略。
  2. 上述抽象类的具体子类。 individualSignIn 支持两种不同的实现。这些是 Google 和 Apple 的个人登录。不同之处在于注入到上述具体类 bean 中的服务(Google 登录服务、Apple 登录服务)。我将在下面展示设置。

所以,Kotlin dsl 如下所示;

bean(name = "googleUserSignIn") {
            IndividualUserSignIn(
                ref("googleUserSignInService"),
                ref("userHibernateDAO"),
                ref("socialAccountHibernateDAO"),
                ref("userService"),
                ...
            )
        }

bean(name = "appleUserSignIn") {
            IndividualUserSignIn(
                ref("appleUserSignInService"),
                ref("userHibernateDAO"),
                ref("socialAccountHibernateDAO"),
                ref("userService"),
                ...
            )
        }

最后,代理请求的策略如下;

bean<UserSignInFactory>()

这些类的实现如下所示; 首先是

AbstractStrategy

abstract class AbstractUserSignIn(
    private val userSignInService: UserSignInService,
    private val userDAO: UserDAO,
    private val socialAccountDAO: SocialAccountDAO,
    private val userService: UserService,
    ....
) {

    @Transactional
    open fun signIn(userSignInRequest: SignInRequest): SignInResult {...}
    
    fun getSignInStrategy(): UserSignInStrategy{ // **(A)**
       return userSignInService.getSignInStrategy()
    }
}

然后继承该类的类;

open class IndividualUserSignIn constructor(
    userSignInService: UserSignInService,
    userDAO: UserDAO,
    socialAccountDAO: SocialAccountDAO,
    userService: UserService,
    ...
) : AbstractUserSignIn(
    userSignInService,
    userDAO,
    socialAccountDAO,
    userService,
    ...
) {

    @PostConstruct
    private fun init()
    {
        println("strategy :" + getSignInStrategy()) // **(B)**
    }
...
}

和工厂类。

@Component
open class UserSignInFactory @Autowired constructor(private val userSignInServices: Set<IndividualUserSignIn>) {

    @PostConstruct
    private fun createStrategies() {
        userSignInServices.forEach { strategy ->
            strategyMap[strategy.getSignInStrategy()] = strategy // **(C)**
        }
    }
    ....
    companion object {
        private val strategyMap: EnumMap<UserSignInStrategy, AbstractUserSignIn> = EnumMap(UserSignInStrategy::class.java)
    }
}

(A)点是问题出现的地方。抽象类使用注入的服务让调用者了解其支持实现。

好吧,问题就在这里。

  1. 在(B)点;在实例化具体策略时,@PostConstruct 按预期工作并打印支持的策略。调试说this是策略的实例本身。
  2. 在(C)点;在遍历集合时,我收到一个 NPE,因为 (A) 点中使用的注入服务看起来为空。这里 set 中的元素是 spring 生成的代理的实例,指向上面步骤 #1 中的实例。
java spring-boot kotlin dependency-injection nullpointerexception
3个回答
1
投票

考虑将具体策略定义为惰性豆。

这将确保策略在工厂中访问之前使用注入的正确依赖项进行实例化。

更新为懒豆:

bean(name = "googleUserSignIn") {
  lazy {
    IndividualUserSignIn(
      ref("googleUserSignInService"),
      ref("userHibernateDAO"),
      ref("socialAccountHibernateDAO"),
      ref("userService"),
      ...
    )
  }
}

bean(name = "appleUserSignIn") {
  lazy {
    IndividualUserSignIn(
      ref("appleUserSignInService"),
      ref("userHibernateDAO"),
      ref("socialAccountHibernateDAO"),
      ref("userService"),
      ...
    )
  }
}

然后更新 UserSignInFactory 类以适应延迟初始化:

@Component
open class UserSignInFactory @Autowired constructor(
  private val userSignInServices: Lazy<Set<IndividualUserSignIn>>) {

  @PostConstruct
  private fun createStrategies() {
    userSignInServices.forEach { strategy ->
      strategyMap[strategy.getSignInStrategy()] = strategy // **(C)**
    }
  }
  ....
  companion object {
    private val strategyMap: 
    EnumMap<UserSignInStrategy, AbstractUserSignIn> = EnumMap(UserSignInStrategy::class.java)
  }
}

0
投票

即使我已经接受了答案,我也想告诉我们我们的想法,并诚实地说正确的实施方法。我们带来了抽象类实现的 SignIn interface 以及该接口中定义的所有 @Transactional 方法。这样,spring就能够代理类(不知道为什么),我们的场景就完成了。


-3
投票

问题似乎与 Spring bean 的初始化顺序有关。特别是,UserSignInFactory bean 在 individualUserSignInService 的 bean 完全初始化之前就已初始化,这会导致在调用 getSignInStrategy() 时 AbstractUserSignIn 中的 userSignInService 字段为 null。 这可以使用 Kotlin DSL 中 bean 方法的 dependent-on 属性来完成:

@DependsOn("googleUserSignInService")
bean(name = "googleUserSignIn") {
    IndividualUserSignIn(
        ref("googleUserSignInService"),
        ref("userHibernateDAO"),
        ref("socialAccountHibernateDAO"),
        ref("userService")
    )
}

@DependsOn("appleUserSignInService")
bean(name = "appleUserSignIn") {
    IndividualUserSignIn(
        ref("appleUserSignInService"),
        ref("userHibernateDAO"),
        ref("socialAccountHibernateDAO"),
        ref("userService"),
        ...
    )
}

是的,dependsOn属性确实不存在,这个例子是我从一段看似错误的代码中拿来的 我使用@DependsOn注解进行修正

© www.soinside.com 2019 - 2024. All rights reserved.