Reputation: 1378
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
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
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.
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>
XmlBeansConfig.scala
import org.springframework.context.annotation.{Configuration, ImportResource}
@Configuration
@ImportResource(value = Array("classpath*:applicationContext.xml"))
class XmlBeansConfig {
}
JobConfig.scala
object JobConfig{
def getInstance = this
@Autowired
var tableResolver: TableResolver = _
}
Upvotes: 0
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
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
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:
Upvotes: 4
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
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
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