Overview

3 The Spring context: Wiring beans

In this chapter, the focus shifts from simply defining beans in the Spring context to making those beans work together. The central idea is that real applications are built from collaborating objects, and Spring helps manage those relationships by supplying references automatically instead of forcing you to retrieve everything manually. This is where dependency injection becomes important, because it lets the framework connect objects and reduce the amount of plumbing code you need to write.

The chapter first shows two ways to connect beans defined with configuration methods: direct wiring and parameter-based injection. With direct wiring, one bean-creation method calls another, and Spring ensures only one instance is used from the context. With parameter injection, Spring resolves a bean and passes it into the method that needs it, which is presented as a form of dependency injection. The chapter also introduces the idea that Spring controls these injections as part of inversion of control, helping keep application code cleaner and easier to manage.

Next, the chapter explains the @Autowired annotation, which can inject dependencies through fields, constructors, or setters. Constructor injection is emphasized as the best choice for production because it supports immutability and makes testing easier, while field injection and setter injection are described as less desirable in real projects. The chapter finishes by warning about circular dependencies, which can cause Spring to fail when two beans require each other during creation, and by showing how Spring chooses among multiple beans of the same type using bean names, @Primary, or @Qualifier.

Having two beans in the Spring context, we want to establish a relationship between them. We do this so that one object can then delegate to the other in the implementation of their responsibilities. You can do this using a wiring approach, which implies directly calling the methods that declare the beans to establish the link between them, or through auto-wiring. You use the framework’s dependency injection capabilities.
Implementing the relationship between the beans. This is a simplified diagram representing the “has-A” relationship between the Person and the Parrot objects. We’ll implement this relationship through wiring and auto-wiring.
We added the two beans in the context to configure the relationship between them further.
We establish the relationship between the beans using direct wiring. This approach implies calling the method that returns the bean you want to set directly. You need to call this method from the one that defines the bean for which you set the dependency.
Spring creates a parrot instance when it calls the first @Bean annotated method parrot(). Then, Spring creates a person instance when it calls the second @Bean annotated method person(). The second method, person(), directly calls the first method, parrot(). Does this mean two instances of type parrot are created?
When two methods annotated with @Bean call each other, Spring knows you want to create a link between the beans. If the bean already exists in the context (3A), Spring returns the existing bean without forwarding the call to the @Bean method. If the bean doesn’t exist (3B), Spring creates the bean and returns its reference.
By defining a parameter to the method, we instruct Spring to provide us with a bean of the type of that parameter from its context. We can then use the provided bean (parrot) when creating the second one (person). This way, we establish the has-A relationship between the two beans.
An application that’s not using the IoC principle controls the execution and makes use of various dependencies. An application using the IoC principle allows a dependency to control its execution. The DI is such an example of control. The framework (a dependency) sets a value into a field of an object of the app.
Using the @Autowired annotation over the field, we instruct Spring to provide a value for that field from its context. Spring creates the two beans, person and parrot, and injects the parrot object to the field of the bean of type Person.
When you define a parameter of the constructor, Spring provides a bean from its context as a value to that parameter when calling the constructor.
A circular dependency. Spring needs to create a bean of type Parrot. But because Parrot has as a dependency a Person, Spring needs first to create a Person. However, to create a Person, Spring already needs to have built a Parrot. Spring is now in a deadlock. It cannot create a Parrot because it needs a Person, and it cannot create a Person because it needs a Parrot.
One way to instruct Spring to provide you with a specific instance from its context, when the context contains more than one instance of the same type, is to rely on the name of this instance. Just name the parameter the same as the instance you’d like Spring to provide you.

Summary

  • In Spring applications, beans usually collaborate with one another, and Spring manages these relationships through dependency injection (DI).
  • In configuration classes, dependencies can be wired either by calling one @Bean method from another or by letting Spring inject dependencies through method parameters.
  • Dependency injection is an implementation of the IoC principle: instead of creating dependencies themselves, objects receive them from the framework.
  • The @Autowired annotation is commonly used for dependency injection in Spring-managed classes.
  • Spring supports field, constructor, and setter injection. Constructor injection is generally preferred because it improves immutability, readability, and testability.
  • Circular dependencies happen when beans depend on each other during creation. These situations usually indicate a design problem and should be refactored.
  • When multiple beans of the same type exist, Spring requires additional information to choose one, typically through @Primary or @Qualifier.

FAQ

What does dependency injection (DI) mean in Spring?Dependency injection is when Spring provides a required object or value to another object, instead of the object creating it manually. Spring resolves the dependency and sets it into a field, constructor parameter, setter, or method parameter.
Why do developers prefer dependency injection over creating objects manually?Dependency injection reduces boilerplate code, makes applications easier to read, and lets Spring manage object relationships. It also improves testability and helps keep code clean and maintainable.
What are the two main ways to create relationships between beans defined with @Bean?You can create relationships by wiring beans directly with a method call from one @Bean method to another, or by using a method parameter and letting Spring autowire the dependency.
What is the role of the @Autowired annotation?@Autowired tells Spring to inject a value from the application context into a field, constructor, or setter. It marks a dependency that Spring should resolve automatically.
What are the three ways to use @Autowired?You can use @Autowired on a field, on a constructor, or on a setter method. These correspond to field injection, constructor injection, and setter injection.
Which injection style is preferred in real projects?Constructor injection is usually preferred in production code. It supports immutability, makes dependencies explicit, and is easier to test than field injection.
Why is field injection usually avoided in production code?Field injection prevents the field from being final, which reduces immutability. It also makes dependencies less explicit and can make testing harder.
What is a circular dependency?A circular dependency happens when bean A depends on bean B, while bean B also depends on bean A. Spring cannot create either bean first, so it gets stuck in a deadlock.
How do you usually fix a circular dependency?Usually by redesigning the classes so they no longer depend on each other directly. The best fix is to remove the cycle from the object relationships.
What happens if multiple beans of the same type exist and Spring must inject one?If there is no clear candidate, Spring may fail with an exception because it does not know which bean to choose. You can resolve this by marking one bean as @Primary or by using @Qualifier to specify the exact bean.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Spring Start Here, Second Edition ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Spring Start Here, Second Edition ebook for free