📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.
✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.
🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.
▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube
Dependency injection (DI) Overview
In Spring, Dependency Injection (DI) is achieved through the Inversion of Control (IoC) container. The container manages the creation and lifecycle of objects and injects the required dependencies into the object when it is created.
The DI mechanism is implemented through constructor injection, setter injection, or field injection. Constructor injection involves passing the required dependencies through the constructor of the object. Setter injection involves injecting dependencies through the object's setter methods, and field injection involves injecting dependencies directly into the object's fields.
The advantages of using dependency injection in Spring include increased modularity, flexibility, and testability of the application. By externalizing the configuration of dependencies, it becomes easier to swap out implementations and test individual components in isolation.
Types of Dependency Injection
- Constructor-based dependency injection
- Setter-based dependency injection
- Field-based dependency injection
1. Constructor-based dependency injection
In the below diagram, the highlighted part shows the Constructor-based dependency injection.
package com.javadevsguide.springframework.di.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.javadevsguide.springframework.di")
public class AppConfiguration {
}
public interface MessageService {
public void sendMsg(String message);
}
@Service("EmailService")
public class EmailService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
@Service("SMSService")
public class SMSService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
@Service("TwitterService")
public class TwitterService implements MessageService{
public void sendMsg(String message) {
System.out.println(message);
}
}
It's time to demonstrate the usage of Constructor-based dependency injection. To avoid decoupling always use interfaces or abstract base classes as instance variables and constructor arguments.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.javadevsguide.springframework.di.service.MessageService;
@Component
public class ConstructorBasedInjection {
private MessageService messageService;
@Autowired
public ConstructorBasedInjection(@Qualifier("TwitterService")
MessageService messageService) {
super();
this.messageService = messageService;
}
public void processMsg(String message) {
messageService.sendMsg(message);
}
}
import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.javadevsguide.springframework.di.config.AppConfiguration; import com.javadevsguide.springframework.di.constructor.ConstructorBasedInjection; public class Application { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class); ConstructorBasedInjection constructorBasedInjection = applicationContext.getBean(ConstructorBasedInjection.class); constructorBasedInjection.processMsg("twitter message sending "); } }
2. Setter-based dependency injection
Setter-based dependency injection example
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.javadevsguide.springframework.di.service.MessageService;
@Component
public class SetterBasedInjection {
private MessageService messageService;
@Autowired
@Qualifier("TwitterService")
public void setMessageService(MessageService messageService) {
this.messageService = messageService;
}
public void processMsg(String message) {
messageService.sendMsg(message);
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.javadevsguide.springframework.di.config.AppConfiguration;
import com.javadevsguide.springframework.di.field.FieldBasedInjection;
public class TestApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class);
SetterBasedInjection setterBasedInjection = applicationContext.getBean(SetterBasedInjection.class);
setterBasedInjection.processMsg("twitter message sending ");
}
}
3. Field-based dependency injection
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import com.javadevsguide.springframework.di.service.MessageService;
@Component
public class FieldBasedInjection {
@Autowired
@Qualifier("TwitterService")
private MessageService messageService;
public void processMsg(String message) {
messageService.sendMsg(message);
}
}
MessageService
package com.javadevsguide.springframework.di.service; public interface MessageService { public void sendMsg(String message);
TwitterService.java
package com.javadevsguide.springframework.di.service;
import org.springframework.stereotype.Service;
@Service("TwitterService")
public class TwitterService implements MessageService {
public void sendMsg(String message) {
System.out.println(message);
}
}
Spring Java Configuration - AppConfiguration.java
Let's create a spring configuration file using the java class AppConfiguration and annotated with @Configuration annotation. This is equivalent to the spring XML configuration file without the beans definition.package com.javadevsguide.springframework.di.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.javadevsguide.springframework.di")
public class AppConfiguration {
}
Test Application with ApplicationContext
package com.javadevsguide.springframework.di.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.javadevsguide.springframework.di.config.AppConfiguration;
import com.javadevsguide.springframework.di.field.FieldBasedInjection;
public class TestApplication {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfiguration.class);
FieldBasedInjection fieldBasedInjection = applicationContext.getBean(FieldBasedInjection.class);
fieldBasedInjection.processMsg("twitter message sending ");
}
}
twitter message sending
2. Field Injection Drawbacks
- Although it is easy to add dependencies this way, we must be careful not to violate the single responsibility principle. Having more dependencies means more responsibilities for a class, which might lead to the difficulty of separating concerns at the refactoring time. The situation when a class becomes bloated is easier to see when dependencies are set using constructors or setters but are quite well hidden when using field injection.
- The responsibility of injecting dependencies is passed to the container in Spring, but the class should clearly communicate the type of dependencies needed using a public interface, through methods or constructors. Using field injections, it can become unclear what type of dependency is really needed and if the dependency is mandatory or not.
- Field injection introduces a dependency on the Spring container, as the @Autowired annotation is a Spring component; thus, the bean is no longer a POJO and cannot be instantiated independently.
- Field injection cannot be used for final fields. This type of field can only be initialized using constructor injection.
- Field injection introduces difficulties when writing tests as the dependencies have to be injected manually.
Conclusion
GitHub Repository
Github Repository: Guide to Dependency Injection in Spring
there is syntax error when you write code , the annotation @Qualifier is disallowed for this location , so you need to put the annotation inside param method like this
ReplyDelete@Autowired
public ConstructorBasedInjection(@Qualifier("EmailService") MessageService messageService) {
super();
this.messageService = messageService;
}