Skip to content
This repository has been archived by the owner on Apr 14, 2022. It is now read-only.

Functional Bean Configuration

Arjen Poutsma edited this page Oct 25, 2012 · 10 revisions

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

Defining beans using FunctionalConfiguration

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.

FunctionalConfigApplicationContext

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

Convenience bean registration methods

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.

Bean references

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 BeanLookupFunctions, 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.

Configuration composition

Importing XML and @Configuration classes

Registering init and destroy functions

Bean Profiles

Clone this wiki locally