Time for injection
2.3 Metadata and injector configuration
2.3.1 XML injection in Spring
Spring’s primary mode of configuration is an XML file. Though Spring offers many alternatives (we saw one with @Autowired), the XML configuration is its most easily identified and common configuration mechanism. Recall our three wonted steps:
■ Creating the injector
■ Obtaining a component instance from it
■ Using the instance to send mail (spamming away!) In Guice this was written as:
Guice.createInjector().getInstance(Emailer.class).send("Hello!");
Translating to Spring yields the following:
Application Injector
Class introspection XML
@Inject public void...
Annotated code
DSL
bind(My.class) .to(Your.class) .in(..)
Programmatic API
addComponent(A) addComponent(B) ...
Figure 2.7 An injector can be configured in several different ways.
BeanFactory injector = new FileSystemApplicationContext("email.xml");
Emailer emailer = (Emailer) injector.getBean("emailer");
emailer.send("Hello!");
Here, BeanFactory refers to the Spring dependency injector. The call to getBean() is analogous to the original call to getInstance(). Both return an instance of Emailer.
One interesting difference is that we use a string key to identify emailers (and are therefore required to downcast3 it into the variable emailer):
Emailer emailer = (Emailer) injector.getBean("emailer");
Notice that sending an email remains exactly the same in both DI libraries:
emailer.send("Hello!");
This is an important point, because it means that using code for its original, intended purpose remains exactly the same regardless of what library you choose. And it under-lines the fact that dependency injection is not an invasive technique.
Note also how an injector is created. Rather than using a Factory, I’ve used construc-tion by hand to create a FileSystemApplicaconstruc-tionContext and assigned it to a variable of type BeanFactory. Both BeanFactory and FileSystemApplicationContext refer to Spring’s injector. The latter is a specific kind of BeanFactory (just like an English-SpellChecker is a specific kind of English-SpellChecker).
The file email.xml contains the configuration required for Spring’s injector to pro-vide us with an emailer correctly wired with its dependencies. Listing 2.2 shows what email.xml looks like, and figure 2.8 shows how this works.
<?xml version="1.0" encoding="UTF-8"?>
<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-2.5.xsd" >
<bean id="emailer" class="Emailer">
<constructor-arg ref="spellChecker"/>
</bean>
<bean id="spellChecker" class="SpellChecker"/>
</beans>
Let’s look at this in some detail. For clarity, we will omit the boilerplate XML schema declaration4 at the top of the file
B
. First, a <bean> tagC
declares a component of3 Downcasting (or casting) is the checked process of converting an instance of a general type into that of a more specific type; this is typically done by assigning the instance to a variable of the target type. In the given case, the general type Object is cast into a more specific type Emailer.
Listing 2.2 Spring’s injector configuration, email.xml
4 An XML schema is a formal description of the structure of an XML document toward a particular purpose.
In this case, it describes the legal form and structure of a Spring configuration file.
Boilerplate to
class Emailer bound to the string identifier "emailer." This identifier is the key used to identify instances of Emailer in the vocabulary of the injector.
<bean id="emailer" class="Emailer">
Recall that in Guice the key served as both identifier as well as the binding to the Emailer class and was implicit. In Spring’s XML, this binding takes the form of an explicit declaration in a <bean> element. Dependencies of Emailer are similarly declared in their own <bean> tags, in this case a SpellChecker:
<bean id="spellChecker" class="SpellChecker">
Now turn your attention back to the first <bean> tag
D
, where the interesting part lies:<bean id="emailer" class="Emailer">
<constructor-arg ref="spellChecker"/>
</bean>
The <constructor-arg> tag tells Spring to use constructor wiring. It also indicates that Emailer instances should be wired with objects identified by a string key,
"spellChecker." SpellChecker itself is declared
E
in another <bean> tag. This method of configuration has the somewhat useful advantage of being explicit about everything, but that is not necessarily a benefit in itself. As you will see later, it can be more flexible under certain circumstances. Also worth mentioning is a more compact rendering that does the same job for us:<beans ...>
<bean id="emailer" class="Emailer">
<constructor-arg><bean class="SpellChecker"/></constructor-arg>
</bean>
</beans>
Application sends email Injector
Emailer
SpellChecker Configuration
AB
Emailer
Figure 2.8 An emailer is wired with a
spellchecker as per configuration by the injector.
In this case, I’ve nested the declaration of SpellChecker inside Emailer’s constructor-argument tag and omitted its identifier altogether. This is nicer for a couple of reasons:
■ The description of SpellChecker is available right inside that of Emailer.
■ SpellChecker is not exposed via its own identifier. Therefore it can be obtained only within the context of a Emailer.
Both these ideas provide us with a neat encapsulation of Emailer and its dependen-cies. Dependency encapsulation means you can’t accidentally wire a SpellChecker meant for an Emailer to some other object (a Klingon editor, for example).
This concept of encapsulation is roughly analogous to class member encapsulation, which you should be familiar with in Java code. Listing 2.3 revisits our original Emailer’s source code for Spring.
public class Emailer {
private SpellChecker spellChecker;
public Emailer(SpellChecker spellChecker) { this.spellChecker = spellChecker;
}
public void send(String text) { spellChecker.check(text);
//send if ok...;
} }
There is no change—except that now I no longer need Guice’s annotations and can safely remove them, leaving me with a pristine, shiny Emailer.