Как использовать Spring Autowired (или вручную) в объекте Scala?

Я пытаюсь использовать Spring со Scala. Я знаю, что Autowired работает с классом Scala, но я использую веб-фреймворк, для которого требуется объект, и я хочу внедрить в него дао. Интересно, как это сделать? Извините, я новичок в Scala, заранее спасибо.

    @Service
    object UserRest extends RestHelper {
        @Autowired
        @BeanProperty
        val userRepository: UserRepository = null;

        .....
    }

    <beans>
         .....
         <bean id="userRest" class="com.abc.rest.UserRest" >
              <!--- this is my attempt to manually wire it --->
              <property name="userRepository" ref="userRepository"/>
         </bean>
    </beans>

person bernardw    schedule 01.12.2011    source источник
comment
зачем тебе здесь объект? в любом случае @Service class Hello { @Autowired var repo:Repository= _} должно работать нормально, если вы настроили сканирование компонентов или используете AnnotationConfigApplicationContext   -  person OlegYch    schedule 02.12.2011


Ответы (8)


По сути, у вас есть две проблемы:

  • Свойство должно быть изменяемым, то есть var, а не val

  • Все методы Scala object являются static, тогда как Spring ожидает методы экземпляра. На самом деле Scala создает класс с методами экземпляра с именем UserRest$ за сценой, и вам нужно сделать его одноэлементный экземпляр UserRest$.MODULE$ доступным для Spring.
    Spring может применять конфигурацию к уже существующим одноэлементным экземплярам, ​​но они должны быть возвращены методом, тогда как UserRest$.MODULE$ это поле. Таким образом, вам нужно создать метод для его возврата.

Итак, что-то вроде этого должно работать:

object UserRest extends RestHelper {
   @BeanProperty
   var userRepository: UserRepository = null;

   def getInstance() = this
   ...
}

.

<bean id="userRest" 
    class="com.abc.rest.UserRest" 
    factory-method = "getInstance">
    <property name="userRepository" ref="userRepository"/>
</bean>

Вы можете заменить <property> на @Autowired, но не можете заменить ручное объявление bean-компонента на @Service из-за проблем с экземпляром singleton, описанных выше.

См. также:

person axtavt    schedule 01.12.2011
comment
Это круто. Интересно, есть ли не-xml способ сделать это с Spring 3.1. - person sourcedelica; 01.12.2011
comment
@ericacm: На самом деле вы можете создать объект @Configuration и вернуть UserRest$.MODULE$ из его @Bean-аннотированного метода. Он уже доступен в Spring 3.0. - person axtavt; 01.12.2011
comment
на самом деле, это говорит о том, что значение не найдено для UserRest $, когда я пытался скомпилировать. к счастью, это работает так же хорошо, когда я делаю def getInstance() = this. большое спасибо, это заняло бы у меня вечность. - person bernardw; 02.12.2011

Все, что на самом деле необходимо, это то, что вы определяете свой объект как класс, а не как объект. Таким образом, Spring создаст его экземпляр.

 @Service
    object UserRest extends RestHelper {
        @Autowired
        @BeanProperty
        val userRepository: UserRepository = null;

        .....
    }
<beans>
         .....
         <bean id="userRest" class="com.abc.rest.UserRest" >
              <!--- this is my attempt to manually wire it --->
              <property name="userRepository" ref="userRepository"/>
         </bean>
    </beans>

Изменение «val» на «var» не требуется (Spring использует отражение, которое игнорирует неизменность). Я почти уверен, что это @BeanProperty также не нужно (Spring автоматически назначит базовое поле).

person Dave Griffith    schedule 02.12.2011

решение axtavt у меня не сработало, но, объединив различные предложения из других ответов, я думаю, что это самое элегантное решение:

object User {
    @Autowired val repo: UserRepository = null

    def instance() = this
}

@Configuration
class CompanionsConfig {
    @Bean def UserCompanion = User.instance
}

<context:component-scan base-package="your-package" />

Несколько заметок:

  • Использование @Configuration гарантирует, что ваши объекты-компаньоны с готовностью подключаются автоматически.
  • Использование @Bean def позволяет избежать громоздких имен, которые Scala дает классу, реализующему объект-компаньон.
  • val работает просто отлично, как упоминал Дэйв Гриффит
  • нет необходимости в Scala @BeanProperty, Spring понимает свойства Scala из коробки (я использую 3.2.2)
person Yuri Shkuro    schedule 23.09.2013

Что я делаю, так это использую AutowiredAnnotationBeanPostProcessor для внедрения объекта во время создания.

Например:

object UserRest extends RestHelper {
    @Autowired
    var userRepository: UserRepository = _

    AppConfig.inject(this)
}

@Configuration
class AppConfig extends ApplicationListener[ContextRefreshedEvent] {

  // Set the autowiredAnnotationBeanPostProcessor when the Spring context is initialized
  def onApplicationEvent(event: ContextRefreshedEvent) {
    autowiredAnnotationBeanPostProcessor =
      event.applicationContext.
        getBean(AnnotationConfigUtils.AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME).
          asInstanceOf[AutowiredAnnotationBeanPostProcessor]
  }
}

object AppConfig {
  var autowiredAnnotationBeanPostProcessor: AutowiredAnnotationBeanPostProcessor = null

  def inject(obj: AnyRef) {
    autowiredAnnotationBeanPostProcessor.processInjection(obj);
  }
}

Теперь вы можете использовать AppConfig.inject() для внедрения любого объекта, чей жизненный цикл не контролируется Spring. Например, объекты JPA и т. д.

person sourcedelica    schedule 01.12.2011

Обычный Autowire из класса, отмеченного @Component или @Bean, будет работать с вышеупомянутыми способами.

Но если вы хотите автоматически подключить интерфейс, расширяющий репозиторий Jpa, убедитесь, что Dao является не объектом, а классом.

ex:

ДАО:

object dao{
 @Autowired val repo: jpaRepo = null
}

Это не сработает (проверено). Я предполагаю, что, поскольку он определен как объект, он создается во время выполнения с репо как нулевое значение, поэтому не сможет автоматически подключить репозиторий jpa.

Вместо этого объявите его как класс и отметьте @Component:

@Component
class dao{
@Autowired val repo: jpaRepo = null
}

Это работает, так как мы позволяем Spring управлять созданием объекта(@component), который правильно автоматически подключает репозиторий jpa.

person Naveen KS    schedule 16.05.2018

Ни один из предыдущих ответов не работал у меня, но после некоторой борьбы я смог решить это следующим образом:

@Service
class BeanUtil extends ApplicationContextAware {

    def setApplicationContext(applicationContext: ApplicationContext): Unit = BeanUtil.context = applicationContext

}

object BeanUtil {
    private var context: ApplicationContext = null

    def getBean[T](beanClass: Class[T]): T = context.getBean(beanClass)
}

затем объект:

object MyObject {
    val myRepository: MyRepository = BeanUtil.getBean(classOf[MyRepository])
}
person Caio Duarte    schedule 30.07.2020

В дополнение к https://stackoverflow.com/a/8344485/5479289 также можно добавить scala упаковать объект в контекст Spring, а также scala object, используя фабричный метод. Скомпилированный объект пакета представляет собой обычный класс Java с именем package, поэтому вы можете добавить его в контекст Spring. После этого у вас будут все возможности Spring внутри этого объекта, т.е. @Autowired, @Value, ручное связывание и т.д.

Тестовый пакет:

package my.module

package object A {
  def getInstance = this

  @Autowired
  private val b: B = null
}

И spring контекст xml:

<beans ...>
  ...
  <bean id="a" class="my.module.A.package" factory-method="getInstance"/>
  ...
</beans>
person Anton Ermakov    schedule 13.11.2015

Я столкнулся с той же проблемой.
У меня есть много служб, и я хочу вызывать эти службы @Autowired из объектов scala.
Я перепробовал все вышеперечисленное, но ни один из них не сработал так, как я ожидал.
У меня есть объект с именем JobConfig.scala и я хочу автоматически связать класс TableResolver и класс TableResolver со многими другими классами.

Мое приложение находится в весенней загрузке и scala.

  1. Добавьте src/main/resources/applicationContext.xml файл.
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="jobId" class="package.JobConfig"
          factory-method="getInstance">
    </bean>

</beans>
  1. Добавить XmlBeansConfig.scala
import org.springframework.context.annotation.{Configuration, ImportResource}

@Configuration
@ImportResource(value = Array("classpath*:applicationContext.xml"))
class XmlBeansConfig {
}
  1. Внутри JobConfig.scala
object JobConfig{
  def getInstance = this

  @Autowired
  var tableResolver: TableResolver = _
}
person Ganesh    schedule 26.03.2020