spring – what is dependency injection and how it works in spring

How Dependency Injection works

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.

E.g.

1
2
3
4
5
6
7
8
9
10
11
12
public class  implements Knight{
private RescueDamselQuest quest;

public () {
quest = new RescueDamselQuest();
}


public void embarkOnQuest() {
quest.embark();
}
}

With DI, objects are given their dependencies at creatiion time by some third party that coordinates each object in the system. Objects aren’t expected to create or obtain their dependencies.

1
2
3
4
5
6
7
8
9
10
11
12
public class BraveKnight implements Knight{
private Quest quest;

public BraveKnight(Quest q) { // Quest is injected
quest = q;
}


public void embarkOnQuest() {
quest.embark();
}
}

BraveKnight is given a quest at construction time as a constructor argument, instead of creating its own. This is a 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 SlayDragonQuest, a MakeRoundTableRounderQuest, or any other Quest implementation he’s given.

The point 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 on, as long as it implements the Quest interface.

One of the most common ways a dependency is swapped out is with a mock implementation during testing

1
2
3
4
5
6
7
8
9
10
public class BraveKnightTest {
@Test
public void knightShouldEmbarkQuest(@Mocked final Quest q) {
BraveKnight bk = new BraveKnight(q);
bk.embarkOnQuest();
new Verifications {{
q.embark(); times = 1;
}};
}
}

Injecting a Quest into a Knight - Wiring

1
2
3
4
5
6
7
8
9
10
11
12
public class SlayDragonQuest implements Quest {
private PrintStream ps;

public SlayDragonQuest(PrintStream p) {
ps = p;
}


public void embark() {
ps.println("Embarking on quest to slay dragon!");
}
}

SlayDragonQuest implements the Quest interface, making it a good fit for BraveKnight. But how can you give SlayDragonQuest to BrageKnight?

The act of creating associations between application components is commonly referred to as wiring.

Java-based configuration

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class KnightConfig {
@Bean
public Quest quest() {
return new SlayDragonQuest(System.out);
}

@Bean
public Knight knight() {
return new BraveKnight(quest());
}
}

Whether you use XML-based or Java-based configuration, the benefits are: although BraveKnight depends on a Quest, it doesn’t know what type of Quest it will be given or where that Quest will come from. This makes it possible to change those dependencies with no changes to the depending classes.

Seeing it work

1
2
3
4
5
6
7
8
9
public class JavaConfigurationMain {
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(KnightConfig.class);
// AbstractApplicationContext context = new ClassPathXmlApplicationContext("app-context.xml");
Knight knight = context.getBean(Knight.class);
knight.embarkOnQuest();
context.close();
}
}

In a Spring application, an application context loads bean definitions and wired them together. The Spring application context is fully responsible for the creation of and wiring of the objects that make up the application.