bernardw
bernardw

Reputation: 1378

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

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.

    @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>

Upvotes: 13

Views: 20407

Answers (8)

Caio Duarte
Caio Duarte

Reputation: 21

None of the previous answears worked for me, but after some struggle I could solve it like this:

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

an then the object:

object MyObject {
    val myRepository: MyRepository = BeanUtil.getBean(classOf[MyRepository])
}

Upvotes: 1

Ganesh
Ganesh

Reputation: 757

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.scala and I want to autowire TableResolver class and TableResolver class itself autowire many other classes.

My application is in spring boot and scala.

  1. Add src/main/resources/applicationContext.xml file.
<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
import org.springframework.context.annotation.{Configuration, ImportResource}

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

  @Autowired
  var tableResolver: TableResolver = _
}

Upvotes: 0

Naveen KS
Naveen KS

Reputation: 11

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

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

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.

Instead declare it as class and mark with @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.

Upvotes: 1

Anton Ermakov
Anton Ermakov

Reputation: 469

In addition to https://stackoverflow.com/a/8344485/5479289, it's also possible to add scala package object to 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.

Test package:

package my.module

package object A {
  def getInstance = this

  @Autowired
  private val b: B = null
}

And spring context xml is:

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

Upvotes: 0

Yuri Shkuro
Yuri Shkuro

Reputation: 784

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

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)

Upvotes: 4

axtavt
axtavt

Reputation: 242686

Basically, you have two problems:

  • Property should be mutable, i.e. var rather than val

  • All methods of Scala object are 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.

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 @Service due to problems with singleton instance described above.

See also:

Upvotes: 17

Dave Griffith
Dave Griffith

Reputation: 20515

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

 @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).

Upvotes: 4

sourcedelica
sourcedelica

Reputation: 24040

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

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.

Upvotes: 1

Related Questions