Injecting dependencies
The phrase dependency injection may sound intimidating, conjuring up notions of a
complex programming technique or design pattern. But as it turns out, DI isn’t nearly
Listing1.2Spring doesn’t make any unreasonable demands on HelloWorldBean.
EJB core
business logic
This is all you
needed
Licensed to Christian Cederquist <chrisman@kaus.dk>
Simplify 7 ing Java development
as complex as it sounds. By applying DI in your projects, you’ll find that your code will
become significantly simpler, easier to understand, and easier to test.
Any nontrivial application (pretty much anything more complex than a Hello
World example) is made up of two or more classes that collaborate with each other to
perform some business logic. Traditionally, each object is responsible for obtaining its
own references to the objects it collaborates with (its dependencies). This can lead to
highly coupled and hard-to-test code.
For example, consider the Knight class shown next.
package com.springinaction.knights;
public classDamselRescuingKnightimplementsKnight{
privateRescueDamselQuestquest;
public DamselRescuingKnight(){
quest=newRescueDamselQuest();
}
public voidembarkOnQuest()throwsQuestException{
quest.embark();
}
}
As you can see, DamselRescuingKnight creates its own quest, a RescueDamselQuest,
within the constructor. This makes a DamselRescuingKnight tightly coupled to a
RescueDamselQuest and severely limits the knight’s quest-embarking repertoire. If a
damsel needs rescuing, this knight’s there. But if a dragon needs slaying or a round
table needs… well…rounding, then this knight’s going to have to sit it out.
What’s more, it’d be terribly difficult to write a unit test for DamselRescuing-
Knight. In such a test, you’d like to be able to assert that the quest’s embark() method
is called when the knight’s embarkOnQuest() is called. But there’s no clear way to
accomplish that here. Unfortunately, DamselRescuingKnight will remain untested.
Coupling is a two-headed beast. On one hand, tightly coupled code is difficult to
test, difficult to reuse, difficult to understand, and typically exhibits “whack-a-mole”
bug behavior (fixing one bug results in the creation of one or more new bugs). On
the other hand, a certain amount of coupling is necessary—completely uncoupled
code doesn’t do anything. In order to do anything useful, classes need to know about
each other somehow. Coupling is necessary, but should be carefully managed.
With DI, on the other hand, objects are given their dependencies at creation time
by some third party that coordinates each object in the system. Objects aren’t
expected to create or obtain their dependencies—dependencies are injected into the
objects that need them.
To illustrate this point, let’s look at BraveKnight in the following listing, a knight
that’s not only brave, but is capable of embarking on any kind of quest that comes
along.
Listing1.3A DamselRescuingKnight can only embark on RescueDamselQuests.
Tightly coupled to
RescueDamselQuest
package com.springinaction.knights;
public classBraveKnightimplementsKnight{
privateQuestquest;
public BraveKnight(Questquest){
this.quest=quest;
}
public voidembarkOnQuest()throwsQuestException{
quest.embark();
}
}
As you can see, unlike DamselRescuingKnight, BraveKnight doesn’t create his own
quest. Instead, he’s given a quest at construction time as a constructor argument. This
is a type of dependency injection known as constructor injection.
What’s more, the quest he’s given is typed as Quest, an interface that all quests
implement. So BraveKnight could embark on a RescueDamselQuest, a SlayDragon-
Quest, a MakeRoundTableRounderQuest, or any other Quest implementation he’s
given.
The point here is that BraveKnight isn’t coupled to any specific implementation of
Quest. It doesn’t matter to him what kind of quest he’s asked to embark upon, so long
as it implements the Quest interface. That’s the key benefit of DI—loose coupling. If
an object only knows about its dependencies by their interface (not by their implemen-
tation or how they’re instantiated), then the dependency can be swapped out with a
different implementation without the depending object knowing the difference.
One of the most common ways that a dependency will be swapped out is with a
mock implementation during testing. You were unable to adequately test Damsel-
RescuingKnight due to tight coupling, but you can easily test BraveKnight by giving it
a mock implementation of Quest, as shown next.
package com.springinaction.knights;
import staticorg.mockito.Mockito.*;
import org.junit.Test;
public classBraveKnightTest{
@Test
public voidknightShouldEmbarkOnQuest()throwsQuestException{
QuestmockQuest=mock(Quest.class);
BraveKnightknight=newBraveKnight(mockQuest);
knight.embarkOnQuest();
verify(mockQuest,times(1)).embark();
}
}
Listing1.4A BraveKnight is flexible enough to take on any Quest he’s given
Listing1.5To test BraveKnight, you’ll inject it with a mock Quest.
Here you’re using a mock object framework known
as Mockito to create a mock implementation of the
Quest interface. With the mock object in hand, you
create a new instance of BraveKnight, injecting the
mock Quest via the constructor. After calling the
embarkOnQuest() method, you ask Mockito to ver-
ify that the mock Quest’s embark() method was
called exactly once.
INJECTING A QUEST INTO A KNIGHT
Now that your BraveKnight class is written in such
a way that you can give him any quest you want,
how can you specify which Quest to give him?
The act of creating associations between appli-
cation components is commonly referred to as wiring.
In Spring, there are many ways to wire components together, but a common
approach has always been via XML. The following listing shows a simple Spring config-
uration file, knights.xml, that gives a BraveKnight a SlayDragonQuest.
<?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-3.0.xsd">
<bean id="knight"class="com.springinaction.knights.BraveKnight">
<constructor-argref="quest"/>
</bean>
<bean id="quest"
class="com.springinaction.knights.SlayDragonQuest"/>
</beans>
This is a simple approach to wiring beans in Spring. Don’t concern yourself too much
with the details right now. We’ll dig more into Spring configuration and see what’s
going on when we get to chapter 2. We’ll also look at other ways that we can wire
beans in Spring.
Now that you’ve declared the relationship between BraveKnight and a Quest, you
need to load up the XML configuration file and kick off the application.
SEEING IT WORK
In a Spring application, an application context loads bean definitions and wires them
together. The Spring application context is fully responsible for the creation of and
wiring of the objects that make up the application. Spring comes with several imple-
mentations of its application context, each primarily differing only in how they load
their configuration.
Listing1.6Injecting a SlayDragonQuest into a BraveKnight with Spring
Inject quest bean
Create SlayDragonQuest
Figure 1.1 Dependency injection
involves giving an object its
dependencies as opposed to an object
having to acquire those dependencies
on its own.
Because the beans in knights.xml are declared in an XML file, an appropriate
choice for application context might be ClassPathXmlApplicationContext. This
Spring context implementation loads the Spring context from one or more XML files
located in the application’s classpath. The main() method in the following listing uses
ClassPathXmlApplicationContext to load knights.xml and to get a reference to the
Knight object.
package com.springinaction.knights;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public classKnightMain{
public staticvoidmain(String[]args){
ApplicationContextcontext=
new ClassPathXmlApplicationContext("knights.xml");
Knightknight=(Knight)context.getBean("knight");
knight.embarkOnQuest();
}
}
Here the main() method creates the Spring application context based on the
knights.xml file. Then it uses the application context as a factory to retrieve the bean
whose ID is knight. With a reference to the Knight object, it calls the embarkOnQuest()
method to have the knight embark on the quest that it was given. Note that this class
knows nothing about which type of Quest our hero has. For that matter, it’s blissfully
unaware of the fact that it’s dealing with BraveKnight. Only the knights.xml file
knows for sure what the implementations are.
No comments:
Post a Comment