提问人:Olgun Kaya 提问时间:6/13/2023 最后编辑:Abdulrahman HasanatoOlgun Kaya 更新时间:6/23/2023 访问量:387
Spring Boot 和 Kotlin DSL 配置
Spring Boot and Kotlin DSL Configuration
问:
最近,我被分配到一个项目,该项目禁用了一些自动配置,并主要使用 KotlinDSL 手动配置 spring boot 应用程序。
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
CassandraDataAutoConfiguration.class,
CassandraAutoConfiguration.class
})
我面临着我相信 Kotlin lang 与 spring 集成的问题。
让我向您展示设置。
- 包含@Transactional方法的抽象登录策略。
- 上述抽象类的具体子类。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)**
}
...
}
和 Factory 类。
@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)是问题出现的地方。抽象类使用注入的服务让调用方了解其支持实现。
好吧,问题来了。
- 在(B)点;在实例化具体策略时,@PostConstruct将按预期工作并打印支持的策略。调试说这是策略本身的实例。
- 在(C)点;在遍历 Set 时,我收到 NPE,因为在点 (A) 中使用的注入服务看起来为 null。在这里,集合中的元素是 Spring 生成的代理的实例,指向上面步骤 #1 中的实例。
答:
-3赞
mahdi hashemi
6/23/2023
#1
似乎问题与Spring bean的初始化顺序有关。具体而言,UserSignInFactory bean 在 IndividualUserSignInService 的 Bean 完全初始化之前就已初始化,这导致在调用 getSignInStrategy() 时 AbstractUserSignIn 中的 userSignInService 字段为 null。 这可以使用 Kotlin DSL 中 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注释进行更正
评论
0赞
Olgun Kaya
6/23/2023
您确定存在 dependsOn 属性吗?
2赞
DavidW
6/25/2023
看起来像 ChatGPT。它可能组成了 dependsOn 属性
1赞
djmonki
6/23/2023
#2
考虑将具体策略定义为懒惰的 bean。
这将确保在工厂访问策略之前,使用注入的正确依赖项来实例化策略。
更新为惰性 bean:
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赞
Olgun Kaya
6/23/2023
虽然这似乎有点棘手,但仍然是解决问题的一种方式。我接受这个作为答案。
评论
bean(name = "googleUserSignIn") { lazy { IndividualUserSignIn( ........ ) } }
(private val userSignInServices: Lazy<Set<IndividualUserSignIn>>)