Sping Boot 和KotlinDSL配置

drkbr07n  于 2023-06-24  发布在  Kotlin
关注(0)|答案(2)|浏览(148)

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

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

我面临着我相信Kotlinlang与Spring集成的问题。
我来给你演示一下。
1.一个抽象的登录策略,其中包含@Transactional方法。
1.上述抽象类的具体子级。IndividualSignIn支持两种不同的实现。这些是谷歌和苹果的个人登录。差异的制造者是服务(Google登录服务,Apple登录服务),它被注入到上面的具体类bean中。我将在下面展示设置。
所以,Kotlindsl就像下面这样;

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是策略的示例本身。
1.在点(C)处;在遍历Set时,我收到了一个NPE,因为在点(A)中使用的注入服务看起来为空。这里set中的元素是spring生成的代理的示例,指向上面步骤#1中的示例。

u1ehiz5o

u1ehiz5o1#

考虑将具体策略定义为lazy bean。
这将确保在工厂中访问策略之前,使用注入的正确依赖关系示例化策略。
更新为lazy beans:

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)
  }
}
mqxuamgl

mqxuamgl2#

这个问题似乎与Springbean的初始化顺序有关。特别是,在IndividualUserSignInService的bean完全初始化之前,UserSignInFactory bean就已经初始化了,这导致在调用getSignInStrategy()时,AbstractUserSignIn中的userSignInService字段为null。这可以使用KotlinDSL中bean方法的depends-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注解进行更正

相关问题