Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
hkushwaha
Product and Topic Expert
Product and Topic Expert
11,214
  • SAP Managed Tags:







Field Injection is not recommended

Within the context of Spring Boot and dependency injection, there is a debate around the best practices for injecting dependencies: Field injection, Setter injection, and Constructor injection.


In this article, we’ll focus on the pitfalls of field injection and make a case for moving away from it.















What is Field Injection?


Field injection involves directly annotating private fields of a class with @Autowired. Here's an example:



@Component
public class OrderService {

@Autowired
private OrderRepository orderRepository;

public Order findOrderById(Long id) {
return orderRepository.findById(id);
}
}

Why You Should Stop Using Field Injection


1. Testability


Field injection complicates the unit testing of your components. Since dependencies are directly injected into the fields, you cannot easily provide mocks or alternative implementations outside of the Spring context.


Lets take the same example of the sameOrderService class.


If you wish to unit test the OrderService, you'd face difficulty in mocking the OrderRepository because it's a private field. Here’s a way to unit test OrderService:



@RunWith(SpringJUnit4ClassRunner.class)
public class OrderServiceTest {

private OrderService orderService;

@Mock
private OrderRepository orderRepository;

@Before
public void setUp() throws Exception {
orderService = new OrderService();

// This will set the mock orderRepository into orderService's private field
ReflectionTestUtils.setField(orderService, "orderRepository", orderRepository);
}

...
}

Though possible, using Reflection to replace the private fields is not a great design. It contradicts object-oriented design principles and makes tests difficult to read and maintain.


On the other hand, with constructor injection:



@Component
public class OrderService {
private final OrderRepository orderRepository;

public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
}

You can easily provide a mock OrderRepository during testing:
OrderRepository mockRepo = mock(OrderRepository.class);
OrderService orderService = new OrderService(mockRepo);

2. Immutability


Field injection makes your beans mutable post-construction. With constructor injection, once an object is constructed, its dependencies remain unchanged.


Example:


The field-injected class:



@Component
public class UserService {
@Autowired
private UserRepository userRepository;
}

Here, the userRepository reference can be re-assigned after the object has been created, breaking the immutability principle.


If we use constructor injection:



@Component
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}

The userRepository field is can be declared final and made immutable post-construction.

3. Tighter coupling with Spring:


Field injection makes your classes more tightly coupled to Spring, as it uses Spring-specific annotations (@Autowired) directly on your fields. This can introduce problems in below scenarios:




  1. Using Outside Spring: Let’s say you’re building a lightweight command-line application that doesn’t use Spring, but you still want to utilize the UserService logic. In this context, the @Autowired annotation means nothing and can't be used to inject dependencies. You'd have to either refactor the class or implement cumbersome workarounds to reuse UserService.

  2. Switching to Another DI Framework: If you decide to switch to a different Dependency Injection framework, like Google Guice, the Spring-specific @Autowired becomes an obstacle. You'd have to refactor every place where you've used Spring-specific annotations, which can be a tedious process.

  3. Readability and Understanding: For a developer not familiar with Spring, encountering the @Autowired annotation might be confusing. They might wonder how dependencies are resolved, leading to a steeper learning curve.


4. NullPointerException


When a class utilizes field injection and is instantiated via its default constructor, the dependent fields remain uninitialized.


Example:



@Component
public class PaymentGateway {
@Autowired
private PaymentQueue paymentQueue;

public void initiate (PaymentRequest request){
paymentQueue.add(request);
...
}
}

public class PaymentService {
public void process (PaymentRequest request) {
PaymentGateway gateway = new PaymentGateway();
gateway.initiate(request);
}
}

Thus, if PaymentGateway is accessed at runtime in this state, a NullPointerException will occur. The only way to manually initialize these fields outside of the Spring context would be to use reflection, which is not advisable for various reasons.



5. Circular Dependencies


Field injection might mask circular dependencies problems, making them harder to catch during development.


Example:


Consider two services, AService and BService, that depend on each other:



@Service
public class AService {
@Autowired
private BService bService;
}

@Service
public class BService {
@Autowired
private AService aService;
}




The above can lead to unexpected behavior and problems in the application.


Using constructor injection, Spring would immediately throw a BeanCurrentlyInCreationException during startup, making you aware of the circular dependency. To solve the circular dependency problem, however, you can lazily load one of the dependencies using @Lazy.















Conclusion


While field injection might seem more concise, its disadvantages far outweigh its brevity. Constructor injection provides clear advantages in terms of testability, immutability, and overall robustness of your application. It aligns well with the SOLID principles, ensuring your Spring Boot applications are maintainable and less error-prone.








1 Comment
Labels in this area