如何在 Scala 对象中使用 Spring Autowired(或手动连接)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8344224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 03:43:40  来源:igfitidea点击:

How to use Spring Autowired (or manually wired) in Scala object?

springscalaliftautowired

提问by bernardw

I am trying to use Spring with Scala. I know Autowired works with Scala class, but I am using a web-framework that requires an object and I want to inject a dao into it. I wonder how to do this? Sorry, I am quite new to Scala, thanks in advance.

我正在尝试将 Spring 与 Scala 一起使用。我知道 Autowired 可以与 Scala 类一起使用,但是我正在使用一个需要对象的网络框架,并且我想向其中注入一个 dao。我想知道如何做到这一点?抱歉,我对 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>

回答by axtavt

Basically, you have two problems:

基本上,你有两个问题:

  • Property should be mutable, i.e. varrather than val

  • All methods of Scala objectare static, whereas Spring expects instance methods. Actually Scala creates a class with instance methods named UserRest$behind the scene, and you need to make its singleton instance UserRest$.MODULE$available to Spring.
    Spring can apply configuration to preexisting singleton instances, but they should be returned by a method, whereas UserRest$.MODULE$is a field. Thus, you need to create a method to return it.

  • 属性应该是可变的,即var而不是val

  • Scala 的所有方法object都是static,而 Spring 需要实例方法。实际上 Scala 创建了一个类,其中包含UserRest$在幕后命名的实例方法,您需要将其单例实例UserRest$.MODULE$提供给 Spring。
    Spring 可以将配置应用于预先存在的单例实例,但它们应该由方法返回,而UserRest$.MODULE$是一个字段。因此,您需要创建一个方法来返回它。

So, something like this should work:

所以,这样的事情应该工作:

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>

You can replace <property>with @Autowired, but cannot replace manual bean declaration with @Servicedue to problems with singleton instance described above.

由于上述单例实例存在问题,您可以替换<property>@Autowired,但不能替换手动 bean 声明@Service

See also:

也可以看看:

回答by Dave Griffith

All that's actually necessary is that you define your object as a class, rather than an object. That way Spring will instantiate it.

真正需要的是将对象定义为一个类,而不是一个对象。这样 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>

Changing the "val" to "var" is unnecessary (Spring uses reflection, which ignores immutability). I'm pretty sure that that @BeanProperty is also unnecessary (Spring will assign to the underlying field, reflectively).

将“val”更改为“var”是不必要的(Spring 使用反射,它忽略了不变性)。我很确定@BeanProperty 也是不必要的(Spring 会反射性地分配给底层字段)。

回答by Yuri Shkuro

axtavt's solution did not work for me, but combining different suggestions from the other answers I think this is the most elegant solution:

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" />

A few notes:

一些注意事项:

  • Using @Configuration ensures that your companion objects are eagerly autowired
  • Using @Bean def avoids having to deal with noisy names Scala gives to the class that implements the companion object
  • val works just fine, as mentioned by Dave Griffith
  • there is no need for Scala's @BeanProperty, Spring understands Scala properties out of the box (I'm using 3.2.2)
  • 使用@Configuration 确保你的伴生对象急切地自动装配
  • 使用@Bean def 可以避免处理 Scala 为实现伴随对象的类提供的嘈杂名称
  • val 工作得很好,正如戴夫格里菲斯所提到的
  • 不需要 Scala 的 @BeanProperty,Spring 可以立即理解 Scala 属性(我使用的是 3.2.2)

回答by sourcedelica

What I do is use AutowiredAnnotationBeanPostProcessor to inject the object at construction time.

我所做的是使用 AutowiredAnnotationBeanPostProcessor 在构建时注入对象。

For example:

例如:

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);
  }
}

Now you can use AppConfig.inject() to inject any object whose lifecycle is not controlled by Spring. For example, JPA Entities, etc.

现在您可以使用 AppConfig.inject() 注入生命周期不受 Spring 控制的任何对象。例如,JPA 实体等。

回答by Naveen KS

Normal Autowire from a class that is marked with @Component or @Bean would work with above mentioned ways.

来自标记为 @Component 或 @Bean 的类的普通 Autowire 可以使用上述方式工作。

But if you want to auto wire an interface extending Jpa repository then make sure the Dao is not an object but class.

但是,如果您想自动连接扩展 Jpa 存储库的接口,请确保 Dao 不是对象而是类。

ex:

前任:

DAO:

道:

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

This won't work (tested). My guess is that since it's defined as an object, gets instantiated at run time with repo as null value, hence won't be able to autowire jpa repo.

这将不起作用(已测试)。我的猜测是,因为它被定义为一个对象,在运行时被实例化,repo 为空值,因此将无法自动装配 jpa repo。

Instead declare it as class and mark with @Component:

而是将其声明为类并用@Component 标记:

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

It works since we are letting spring to manage the object creation(@component) that autowires jpa repo properly.

它之所以有效,是因为我们让 spring 管理正确自动装配 jpa repo 的对象创建(@component)。

回答by Ganesh

I faced same issue.
I have many services and want to call these @Autowired service from scala objects.
I tried all the above, None of them worked as my expectation.
I have an object named JobConfig.scalaand I want to autowire TableResolverclass and TableResolverclass itself autowire many other classes.

我遇到了同样的问题。
我有很多服务,想从 Scala 对象调用这些 @Autowired 服务。
我尝试了以上所有方法,但都没有达到我的预期。
我有一个命名的对象JobConfig.scala,我想自动装配TableResolver类和TableResolver类本身自动装配许多其他类。

My application is in spring boot and scala.

我的应用程序在 spring boot 和 Scala 中。

  1. Add src/main/resources/applicationContext.xmlfile.
  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. Add XmlBeansConfig.scala
  1. 添加 XmlBeansConfig.scala
import org.springframework.context.annotation.{Configuration, ImportResource}

@Configuration
@ImportResource(value = Array("classpath*:applicationContext.xml"))
class XmlBeansConfig {
}
  1. Inside JobConfig.scala
  1. 里面 JobConfig.scala
object JobConfig{
  def getInstance = this

  @Autowired
  var tableResolver: TableResolver = _
}

回答by Anton Ermakov

In addition to https://stackoverflow.com/a/8344485/5479289, it's also possible to add scala package objectto Spring context, as well as scala object, using factory method. Compiled package object is usual java class named package, so you can add it to Spring context. After that you will have all Spring's possibilities inside this object, i.e @Autowired, @Value, manual wiring etc.

除了https://stackoverflow.com/a/8344485/5479289 之外,还可以使用工厂方法将 scala包对象以及 scala对象添加到 Spring 上下文中。编译后的包对象通常是名为package 的java 类,因此您可以将其添加到 Spring 上下文中。之后,您将在此对象中拥有 Spring 的所有可能性,即@Autowired@Value、手动接线等。

Test package:

测试包:

package my.module

package object A {
  def getInstance = this

  @Autowired
  private val b: B = null
}

And spring context xml is:

而 spring 上下文 xml 是:

<beans ...>
  ...
  <bean id="a" class="my.module.A.package" factory-method="getInstance"/>
  ...
</beans>