Every now and then, there’s an angry post or comment bitching about how the Spring framework is full of XML, how terrible and verbose it is, and how the author would never use it because of that. Of course, that is completely crap. First, when Spring was created, XML was pretty hot. J2EE deployment descriptors (yes, that was the name at the time) was XML-based.
Anyway, it’s 2017 folks, and there are multiple ways to skin a cat. This article aims at listing the different ways a Spring application context can be configured so as to enlighten the aforementioned crowd - and stop the trolling around Spring and XML.
XML
XLM has been the first way to configure the Spring application context. Basically, one create an XML file with a dedicated namespace. It’s very straightforward:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="foo" class="ch.frankel.blog.Foo">
<constructor-arg value="Hello world!" />
</bean>
<bean id="bar" class="ch.frankel.blog.Bar">
<constructor-arg ref="bar" />
</bean>
</beans>
The next step is to create the application context, using dedicated classe:
ApplicationContext ctx = new ClassPathXmlApplicationContext("ch/frankel/blog/context.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("/opt/app/context.xml");
ApplicationContext ctx = new GenericXmlApplicationContext("classpath:ch/frankel/blog/context.xml");
XML’s declarative nature enforces simplicity at the cost of extra verbosity. It’s orthogonal to the code - it’s completely independent. Before the coming of JavaConfig, I still favored XML over self-annotated classes.
Self-annotated classes
As for every new future/technology, when Java 5 introduced annotations, there was a rush to use them. In essence, a self-annotated class will be auto-magically registered into the application context.
To achieve that, Spring provides the @Component
annotation.
However, to improve semantics, there are also dedicated annotations to differentiate between the 3 standard layers of the layered architecture principle:
@Controller
@Service
@Repository
This is also quite straightforward:
@Component
public class Foo {
public Foo(@Value("Hello world!") String value) { }
}
@Component
public class Bar {
@Autowired
public Bar(Foo foo) { }
}
To scan for self-annotated classes, a dedicated application context is necessary:
ApplicationContext ctx = new AnnotationConfigApplicationContext("ch.frankel.blog");
Self-annotated classes are quite easy to use, but there are some downsides:
- A self-annotated class becomes dependent on the Spring framework. For a framework based on dependency injection, that’s quite a problem.
- Usage of self-annotations blurs the boundary between the class and the bean. As a consequence, the class cannot be registered multiple times, under different names and scopes into the context.
- Self-annotated classes require autowiring, which has downsides on its own.
Java configuration
Given the above problems regarding self-annotated classes, the Spring framework introduced a new way to configure the context: JavaConfig.
In essence, JavaConfig configuration classes replace XML file, but with compile-time safety instead of XML-schema runtime validation.
This is based on two annotations @Configuration
for classes, and @Bean
for methods.
The equivalent of the above XML is the following snippet:
@Configuration
public class JavaConfiguration {
@Bean
public Foo foo() {
return new Foo("Hello world!");
}
@Bean
public Bar bar() {
return new Bar(foo());
}
}
JavaConfig classes can be scanned like self-annotated classes:
ApplicationContext ctx = new AnnotationConfigApplicationContext("ch.frankel.blog");
JavaConfig is the way to configure Spring application: it’s orthogonal to the code, and brings some degree of compile-time validation.
Groovy DSL
Spring 4 added a way to configure the context via a Groovy Domain-Specific Language.
The configuration takes place in a Groovy file, with the beans
element as its roots.
beans {
foo String, 'Hello world!'
bar Bar, foo
}
There’s an associated application context creator class:
ApplicationContext ctx = new GenericGroovyApplicationContext("ch/frankel/blog/context.groovy");
I’m not a Groovy developer, so I never used that option. But if you are, it makes a lot of sense.
Kotlin DSL
Groovy has been unceremoniously kicked out of the Pivotal portfolio some time ago. There is no correlation, Kotlin has found its way in. It’s no wonder that the the upcoming release of Spring 5 provides a Kotlin DSL.
package ch.frankel.blog
fun beans() = beans {
bean {
Foo("Hello world!")
Bar(ref())
}
}
Note that while bean declaration is explicit, wiring is implicit, as in JavaConfig @Bean
methods with dependencies.
In opposition to configuration flavors mentioned above, the Kotlin DSL needs an existing context to register beans in:
import ch.frankel.blog.beans
fun register(ctx: GenericApplicationContext) {
beans().invoke(ctx)
}
I didn’t use Kotlin DSL but to play a bit with it for a demo, so I cannot say for sure about pros/cons.
Conclusion
So far, the JavaConfig alternative is my favorite: it’s orthogonal to the code and provides some degree of compile-time validation. As a Kotlin enthusiast, I’m also quite eager to try the Kotlin DSL in large projects to experience its pros and cons first-hand.