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

8

13

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>
Astto answered 1/12, 2011 at 15:54 Comment(1)
why do you need an object here? anyway @Service class Hello { @Autowired var repo:Repository= _} should work fine, assuming you've configured component-scan or using AnnotationConfigApplicationContextOrtolan
N
17

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:

Novelist answered 1/12, 2011 at 16:9 Comment(3)
That's pretty cool. I wonder if there is a non-xml way of doing it with Spring 3.1.Abiogenetic
@ericacm: Actually, you can create an @Configuration object and return UserRest$.MODULE$ from its @Bean-annotated method. It's already available in Spring 3.0.Novelist
actually, it says value not found for UserRest$ when I tried to compile. luckily, it works just as well when i do def getInstance() = this. thanks a bunch, this would have took me forever.Astto
D
4

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

Dumbarton answered 2/12, 2011 at 2:4 Comment(0)
L
4

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)
Litha answered 23/9, 2013 at 0:5 Comment(0)
A
1

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.

Abiogenetic answered 1/12, 2011 at 16:45 Comment(0)
B
1

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.

Bolton answered 16/5, 2018 at 16:21 Comment(0)
F
1

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])
}
Fertility answered 30/7, 2020 at 21:36 Comment(0)
A
0

In addition to https://mcmap.net/q/853926/-how-to-use-spring-autowired-or-manually-wired-in-scala-object, 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>
Antionetteantioxidant answered 13/11, 2015 at 15:24 Comment(0)
D
0

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 = _
}
Dichasium answered 26/3, 2020 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.