最近,我被分配到一个项目,该项目禁用一些自动配置并主要使用 KotlinDSL 手动配置 Spring Boot 应用程序。
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
JpaRepositoriesAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
CassandraDataAutoConfiguration.class,
CassandraAutoConfiguration.class
})
我面临着我相信 Kotlin lang 与 Spring 集成的问题。
让我向您展示设置。
所以,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)点是问题出现的地方。抽象类使用注入的服务让调用者了解其支持实现。
好吧,问题就在这里。
考虑将具体策略定义为惰性豆。
这将确保策略在工厂中访问之前使用注入的正确依赖项进行实例化。
更新为懒豆:
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)
}
}
即使我已经接受了答案,我也想告诉我们我们的想法,并诚实地说正确的实施方法。我们带来了抽象类实现的 SignIn interface 以及该接口中定义的所有 @Transactional 方法。这样,spring就能够代理类(不知道为什么),我们的场景就完成了。
问题似乎与 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注解进行修正