Как мне использовать Spring Beans внутри Scala объекта?

Вопрос или проблема

У меня есть код на Scala, который выглядит примерно так

@Component
class A (@Autowired val componentB: B) {Что-то}

@Component
class B (@Autowired val beanC: C) {Что-то}

@Component
class D () { 
  @Value("${configProperty}")
  val configProperty = ""

  @Bean
  def foobar: C = {Что-то}
}

@SpringBootApplication
class MainClass extends ApplicationRunner {
  def main(): Unit = {
    SpringApplication.run(classOf[MainClass])
  }
  override def run(): Unit = {Ничего не делать}
}

@EnableAutoConfiguration
@Component
object SparkRunnable extends MainClass {
  override def run(): Unit = {
    val annotationContext = new AnnotationConfigApplicationContext(classOf[SparkRunnable])
    val wiredClass = annotationContext.getBean(classOf[A])
  }
}

Когда я пытаюсь запустить код, определенный выше, я получаю

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Ошибка создания бина с именем 'A': Неудовлетворенная зависимость, выраженная через параметр конструктора 0; вложенное исключение - org.springframework.beans.factory.NoSuchBeanDefinitionException: Нет подходящего бина типа 'B': ожидался хотя бы 1 бин, который соответствует кандидату на автодополнение. Аннотации зависимости: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Мне нужно, чтобы SparkRunnable был объектом, потому что он должен выполняться в Spark. Я вижу, что все бины и классы компонентов устанавливаются правильно из отладки и журналов, но я просто не могу получить бины таким образом, чтобы использовать их внутри объекта SparkRunnable.

Я попытался подключить модули вручную, как ниже, но у него даже нет контекста для получения свойств, сконфигурированных в yaml файлах.

val uselessWiringVal = annotationContext.getBean(classOf[D])
val uselessWiringObj = new B(uselessWiringVal.foobar)
val wiredClass = new A(uselessWiringObj)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Ошибка создания бина с именем 'A', определенного по URL[SparkRunnable]: Неудовлетворенная зависимость, выраженная через параметр конструктора 0; вложенное исключение - org.springframework.beans.factory.BeanCreationException: Ошибка создания бина с именем 'D': Инъекция зависимостей, отмеченных как автозаполняемые, не удалась; вложенное исключение - java.lang.IllegalArgumentException: Не удалось разрешить плейсхолдер 'configProperty' в значении "${configProperty}"

Я начинающий в Scala и, вероятно, сделал ошибку, пытаясь использовать SpringBoot для этой работы. Мои вопросы:

  1. Есть ли что-то неправильное в том, как я настраиваю свои классы компонентов?
  2. Как мне фактически получить компоненты для использования из моего RunnableObject?

Было бы идеально сделать что-то вроде
object SparkRunnable (@Autowired A) extends MainClass {}
но такой синтаксис не работает.

Я могу получить компонент с автозаполнением в классе SparkRunnable, но не могу найти способ передать его в объект-компаньон.

Ответ или решение

Чтобы использовать Spring Beans внутри объекта Scala, необходимо учитывать некоторые особенности взаимодействия между Spring и Scala. Эта задача может показаться сложной для начинающих, однако с правильным подходом вы сможете успешно интегрировать Spring в ваше приложение на Scala. Рассмотрим ваши вопросы подробно.

Ответ на вопросы

1. Есть ли ошибки в настройке компонентных классов?

Ваши компонентные классы имеют правильный синтаксис для использования аннотаций Spring (например, @Component, @Autowired, @Value). Однако вы сталкиваетесь с двумя основными проблемами:

  • Недостаточная конфигурация контекста: Ваше приложение может не находить компоненты из-за неправильной конфигурации контекста. Убедитесь, что все необходимые компоненты правильно сканируются и создаются в контексте Spring.

  • Неинициализированные свойства: Ошибка Could not resolve placeholder 'configProperty' указывает на то, что свойство configProperty не было найдено в ваших конфигурационных файлах, таких как application.yml или application.properties. Убедитесь, что данный файл правильно загружается вашим приложением.

2. Как получить компоненты для использования в объекте SparkRunnable?

В Scala нет прямой возможности инъекции зависимостей в объекты (companion objects) таким же образом, как это делается в классах. Однако вы можете использовать несколько обходных путей:

Использование @Component для класса и объектного контекста

Вместо расположения логики внутри объекта SparkRunnable, используйте класс, чтобы иметь возможность полагается на механизм инъекции Spring.

Пример:

@Component
class SparkRunnable(@Autowired val a: A) {
  def runJob(): Unit = {
    // Здесь вы можете использовать 'a' для работы с вашим приложением
  }
}

Теперь вы можете создать этот класс через Spring контекст, а затем выполнять нужный метод runJob.

Изменение вашей основной конфигурации

Вот как может выглядеть ваша основная конфигурация, чтобы всё заработало правильно:

@SpringBootApplication
class MainClass extends ApplicationRunner {
  override def run(args: String*): Unit = {
    // Здесь можно добавить логику, если необходимо
  }
}

object Application {
  def main(args: Array[String]): Unit = {
    val ctx = SpringApplication.run(classOf[MainClass])
    val sparkRunnable = ctx.getBean(classOf[SparkRunnable])
    sparkRunnable.runJob()
  }
}

Таким образом, в MainClass, вы инициализируете контекст и можете получить все необходимые бины и запустить нужное вам приложение.

Заключение

Ваши компоненты в целом настроены правильно, но из-за специфик Scala и внедрения зависимостей с помощью Spring, лучше всего использовать классы вместо объектов для инъекции. Убедитесь, что все конфигурационные параметры корректно загружены, и пробуйте использовать @Component для классов, чтобы получить доступ к необходимым бинам через контекст. Возможно, потребуется внести изменения в способ загрузки конфигурации для устранения ошибки configProperty.

С помощью правильного подхода вы сможете успешно интегрировать Spring и Scala и создать высококачественное приложение.

Оцените материал
Добавить комментарий

Капча загружается...