-
Notifications
You must be signed in to change notification settings - Fork 124
Functional Bean Configuration
#Functional Bean Configuration
As an alternative to defining beans in XML, Spring Scala offers an alternative that uses Scala classes instead of XML files to configure your Spring beans. This approach is similar to using @Configuration in standard Spring, except that it is based on functions rather than annotations.
To create a functional Spring configuration, you simply have to mix in the FunctionalConfiguration
trait into your configuration class.
Beans are defined by calling the bean
method on the trait and passing on a function that creates the bean.
In its simplest form, a functional configuration will look something like the following:
class Person(val firstName: String, val lastName: String) {
var father: Person = _
var mother: Person = _
}
class PersonConfiguration extends FunctionalConfiguration {
bean() {
new Person("John", "Doe")
}
}
This configuration will register a singleton Person bean under a auto-generated name. The preceding configuration is exactly equivalent to the following Spring XML:
<beans>
<bean class="Person">
<constructor-arg value="John"/>
<constructor-arg value="Doe"/>
</bean>
</beans>
Of course, you can also register a bean under a specific name, provide aliases, or set the scope:
class PersonConfiguration extends FunctionalConfiguration {
bean("john", aliases = Seq("doe"), scope = BeanDefinition.SCOPE_PROTOTYPE) {
new Person("John", "Doe")
}
}
This configuration will register a Person under the names "john" and "doe", and also sets the scope to prototype.
In much the same way that Spring XML files are used as input when instantiating a ClassPathXmlApplicationContext
and @Configuration
classes are used for the AnnotationConfigApplicationContext
, a FunctionalConfiguration
can be used as input when instantiating an FunctionalConfigApplicationContext:
object PersonConfigurationDriver extends App {
val applicationContext = new FunctionalConfigApplicationContext(classOf[PersonConfiguration])
val john = applicationContext.getBean(classOf[Person])
println(john.firstName)
}
As an alternative to using the bean method to register a prototype, you can also use the convenience method prototype
.
Like so:
class PersonConfiguration extends FunctionalConfiguration {
prototype("john") {
new Person("John", "Doe")
}
}
In addition to prototype
, you can use the singleton
method to register a singleton.
Even though we have not used it so far, the bean()
method actually has a return value which can be captured in a variable.
The return type of bean()
is a BeanLookupFunction[T]
, where T
is the class of the bean defined.
A BeanLookupFunction
is essentially a Scala function that takes no parameters, and looks up the defined bean when invoked.
You can use the return value to refer to other beans in the functional configuration, like so:
class PersonConfiguration extends FunctionalConfiguration {
val jack = bean() {
new Person("Jack", "Doe")
}
val jane = bean() {
new Person("Jane", "Doe")
}
val john = bean() {
val john = new Person("John", "Doe")
john.father = jack()
john.mother = jane()
john
}
}
In the example above, we define three beans ("jack", "jane", and "john"), and capture these in three vals.
In the bean function where "john" is defined, we set his parents by referring to the "jack" and "jane" vals.
Because these vals are BeanLookupFunction
s, we need to invoke the function (hence the brackets) to do a bean lookup and get the Person instances.
The reason bean()
returns a function () => T
as opposed to just T
has to do with bean scoping.
If bean()
would return T, and we would capture that in a variable, that variable would hold the same bean reference each time it is used, even when the bean would have non-singleton scope.
By returning a function, we make sure that a bean lookup is performed every time a bean is referenced, and instantiate a new instance if required.
Note that the singleton()
convenience method does return T
, because it can refer to the same bean instance each time it is referenced.