Bean Definitions
As mentioned earlier, simple POJOs become powerful inside a Spring container with appropriate metadata (configuration instructions). Supplying metadata to the Spring container can be done by using XML, Java, and Java Annotations. Using these configuration instructions, Spring constructs Java objects (here after referred to as Spring beans, or for short, beans) and maintains them inside the container. Spring does this by using Dependency Injection or IoC capability, which the Spring Framework provides. In this book, those terms are used interchangeably but point to the same design pattern.
NOTE: To configure the beans, you can use the XML, Java, or Annotation method. Spring supports mixing these methods in an application as needed. In version 3.x of Spring, XML configuration can be completely removed from an application, which can be done by using pure Java or using Java Annotations. Spring configuration is explained in detail in Chapters 4 and 5 because of its significance in Spring Framework as a whole.
Dependency Injection (DI) or Inversion of Control (IoC)
There are three main styles of dependency injection: Constructor Injection, which I refer to as type 3 IoC; Setter Injection, type 2 IoC; and Interface Injection, type 1 IoC. Using IoC, your classes don’t look up or instantiate the classes they depend on. Rather, the control is inverted, and the Spring container sets the dependencies. Using IoC often leads to much cleaner code and provides an excellent way to decouple dependent classes.
The benefits of Dependency Injection are:
- Unit testable
- Explicit dependencies
- Consistency
- No plumbing code to write
- Pluggability
- Zero cost of programming to interfaces.
Dependency injection is not effective when the following conditions apply:
- You will never need a different implementation.
- You will never need a different configuration.
If you know you will never change the implementation or configuration of some dependency, there is no benefit in using dependency injection. This is true for both member variables and local variables.
The following sections explain in detail the three main DI types and how they are achieved using XML configuration in Spring.
Setter Injection
Classes are typically JavaBeans, with a no-arg constructor, and with setters for the IoC container to use when wiring dependencies. While Spring supports constructor-based injection, a large number of constructor arguments can be difficult to manage. Spring supports both constructor and setter injection, but its developers tend to prefer setter injection. It is achieved by calling setters on your beans after invoking a no-argument constructor to instantiate your bean.
Listing 3-1. Bean configuration in XML file
1 2 3 4 5 6 7 |
<bean id="loyaltyService" class="com.mybook.loyalty.service.impl.LoyaltyServiceImpl"> <property name="customerRepository" ref="customerRepository"/> </bean> <bean id="customerRepository"/> |
Listing 3-2. Java code corresponding to the preceding XML configuration
1 2 3 4 5 |
JdbcCustomerRepository customerRepository = new JdbcCustomerRepository(); LoyaltyServiceImpl loyaltyService = new LoyaltyServiceImpl(); loyaltyService.setCustomerRepository(customerRepository); |
Constructor Injection
Classes contain constructors with a number of arguments. In most cases, Spring’s IoC container discovers and invokes the constructor based on the number of arguments and their object types. This approach guarantees that a bean is not created in an invalid state. Although Spring generally advocates usage of setter-based dependency injection as much as possible, it fully supports the constructor-based approach as well, because you may want to use it with pre-existing beans that provide only multi-argument constructors, and no setters.
Listing 3-3. Bean configuration in XML file
1 2 3 4 5 6 7 |
<bean id="loyaltyService" class="com.mybook.loyalty.service.impl.LoyaltyServiceImpl"> <constructor-arg ref="customerRepository"/> </bean> <bean id="customerRepository" class="com.mybook.loyalty.customer.JdbcCustomerRepository"/> |
Listing 3-4. Corresponding Java code to the above XML configuration
1 2 3 |
JdbcCustomerRepository customerRepository = new JdbcCustomerRepository(); LoyaltyServiceImpl loyaltyService = new LoyaltyServiceImpl(customerRepository); |
Interface Injection
The exported module provides an interface that its users must implement in order to get the dependencies at runtime. With interface injection, an interface explicitly defines the point where a dependency can be set. This can be confusing at first, but Listing 3-5 gives you a fairly good idea of the interface injection.
Listing 3-5. Interface injection in action
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
interface InjectCustomerAccount { public void injectHere(CustomerAccount account); } class Customer implements InjectCustomerAccount { CustomerAccount injectedCustomerAccount; public void injectHere(CustomerAccount account) { this. injectedCustomerAccount = account; } } |
Constructor and Setter Injection Combined
You can use setter and constructor dependency combined in your configuration without any problem. Listing 3-6 provides an example.
Listing 3-6. Bean configuration in XML configuration
1 2 3 4 5 6 7 8 9 10 11 |
<bean id="loyaltyService" class="com.mybook.loyalty.service.impl.LoyaltyServiceImpl"> <constructor-arg ref="customerRepository"/> <property name="shipmentRepository" ref="shipmentRepository"/> </bean> <bean id="customerRepository" class="com.mybook.loyalty.customer.JdbcCustomerRepository"/> <bean id="shipmentRepository" class="com.mybook.loyalty.customer.JdbcShipmentRepository"/> |
Listing 3-7. Java Code corresponding to the preceding XML configuration
1 2 3 4 5 6 7 |
JdbcCustomerRepository customerRepository = new JdbcCustomerRepository(); JdbcShipmentRepository shipmentRepository = new JdbcShipmentRepository(); LoyaltyServiceImpl loyaltyService = new LoyaltyServiceImpl(customerRepository); loyaltyService.setShipmentRepository(shipmentRepository); |
When to Use Constructors vs. Setters?
Interface injection is more invasive, because you have to write a lot of interfaces to get things all sorted out. But it’s a lot of work to assemble components and dependencies, which is why Spring Framework goes with setter and constructor injection.
Constructor injection is particularly useful when you absolutely must have an instance of the dependency class before your component is used. Use constructor DI to enforce mandatory dependencies. This is a concise way of programming, and injection can be done with a line of code. This can be used to promote immutability by assigning dependencies to final fields.
Use Setter DI to set optional dependencies and to set defaults. Another benefit of setter injection is that it allows dependencies to be declared on an interface.
Page Visitors: 2588
Tomcy John
Latest posts by Tomcy John (see all)
- A Guide to Continuous Improvement for Architects - February 2, 2023
- Cloud-first Architecture Strategy - January 26, 2023
- Architecture Strategy and how to create One - January 24, 2023