Spring Boot Sping Boot 依赖注入不适用于子类,调用时未初始化值

ecbunoof  于 2023-03-23  发布在  Spring
关注(0)|答案(1)|浏览(177)

我想将som依赖注入到我的项目中的一些子类中,但它并没有像我想象的那样工作...我使用的是Sping Boot 3.0.4Java 17编写于Kotlin1.8.0
我的类看起来如下:

@Service
sealed class Integration {

  @Autowired lateinit var smsClient: SmsClient

  @Autowired lateinit var emailClient: EmailClient

  abstract val reference: UUID
  abstract val integrationType: IntegrationType
  abstract fun send(incomingMessage: String)
  abstract fun getContactPoints(): List<String>
}

@Schema(description = "Integration of sms notifications.", allOf = [Integration::class])
class SmsIntegration(
    override val reference: UUID = UUID.randomUUID(),
    override val integrationType: IntegrationType = IntegrationType.SMS_INTEGRATION,
    val countryCode: String,
    val phoneNumbers: List<String>,
) : Integration() {

  @PostConstruct
  override fun send(incomingMessage: String) {
    phoneNumbers.forEach { smsClient.postSms(SmsRequest(countryCode, it, incomingMessage)) }
  }
  override fun getContactPoints(): List<String> = this.phoneNumbers

  fun copy(phoneNumbers: List<String>): SmsIntegration =
      SmsIntegration(
          reference = this.reference, countryCode = this.countryCode, phoneNumbers = phoneNumbers)
}

@Schema(description = "Integration of email notifications.", allOf = [Integration::class])
class EmailIntegration(
    override val reference: UUID = UUID.randomUUID(),
    override val integrationType: IntegrationType = IntegrationType.EMAIL_INTEGRATION,
    val emails: List<String>,
    val subject: String,
) : Integration() {

  @PostConstruct
  override fun send(incomingMessage: String) {
    val emailAddresses: List<EmailAddress> = emails.map { EmailAddress(email = it) }
    val email =
        Email(
            from = EmailAddress(email = "test@test.org"),
            to = emailAddresses,
            subject = subject,
            body = incomingMessage)

    emailClient.sendEmail(email)
  }
  override fun getContactPoints(): List<String> = this.emails

  fun copy(emails: List<String>): EmailIntegration =
      EmailIntegration(reference = this.reference, subject = this.subject, emails = emails)
}

@Schema(description = "Integration of slack notifications.", allOf = [Integration::class])
class SlackIntegration(
    override val reference: UUID = UUID.randomUUID(),
    override val integrationType: IntegrationType = IntegrationType.SLACK_INTEGRATION,
    val channelIds: List<String>
) : Integration() {

  fun copy(channelIds: List<String>): SlackIntegration =
      SlackIntegration(reference = this.reference, channelIds = channelIds)

  @PostConstruct
  override fun send(incomingMessage: String) {
    channelIds.forEach { SlackBot.sendMessage(channelId = it, message = incomingMessage) }
  }

  override fun getContactPoints(): List<String> = this.channelIds
}

正如你所看到的,我有两个自动连接的客户端,一个用于sms,一个用于email,在我的 Integration 类中。我宁愿把它们放在单独的类中,但是我不想在创建这些类时在代码周围的构造函数中传递emailClients和smsclients。所以我选择把它们放在 Integration 类中,但是在 SmsIntegrationEmailIntegration 类中调用**send()**函数时,它们不会初始化。
我觉得我在Sping Boot 中的依赖注入方面以一种糟糕的方式构建了我的代码。只使用 slackBot 很好很容易,因为它是一个单例,但是添加了依赖项,它使它变得一团糟。
任何帮助重组或使这项工作是赞赏。

2q5ifsrm

2q5ifsrm1#

我不确定@Schema注解,我假设它使类成为组件
当你在做继承和Autowired的时候,确保用@set:Autowired在setter上设置注解,在这种情况下你甚至不需要用service标记你的超类,
我认为在超类中获取实现是一个坏主意,正如在评论中提到的,@PostConstruct并不意味着这样(我认为在这种情况下你想使用@EventListener
下面是另一种方法:

// Integration.kt
interface Message
interface Integration {
    fun send(message: Message)
    fun supports(message: Message): Boolean
}

// SMSIntegration.kt
class SMS(val phoneNumber: String) : Message
@Service
class IntegrationSMS : Integration {
    override fun send(message: Message) {
        message as SMS
        // send it
    }

    // implements send(SMS)
    override fun supports(message: Message) = message is SMS
}

// SlackIntegration.kt
class SlackMessage : Message
@Service
class IntegrationSlack : Integration {
    override fun send(message: Message) {
        message as SlackMessage
    }

    // implements send(SlackMessage)
    override fun supports(message: Message) = message is SlackMessage
}

// IntegrationManager.kt
@Service
class IntegrationManager(@Autowired  val integrators: List<Integration> ){

   @EventListener
   fun sendMessage(message: Message){
       for(integration in integrators)
          if(integration.supports(message)
             integration.send(message)
   }

}

现在你可以在代码中的任何地方执行以下操作:

@Service
class SomewhereElse( val publisher: ApplicationEventPublisher){

   fun doSomethingAndSendSMS(){
     ... 
     publisher.publish(SMS("1234"))
   }

   fun doSomethingAndSendSlackMessage(){
     ... 
     publisher.publish(SlackMessage())
   }

}

如果你把@EventListener放在一边,你可以直接调用IntegrationManager.sendMessage,这是SpringSecurity使用AuthenticationProviderAuthenticationManager和几个AuthenticationTokens的实际方法(据我所知,在servlet中

相关问题