Spring Boot Tutorial
Spring Boot - Software Setup and Configuration (STS/Eclipse/IntelliJ)
Prerequisite (Spring Core Concepts)
Spring Boot Core
Spring Boot with REST API
Spring Boot with Database and Data JPA
Spring Boot with Kafka
Spring Boot with AOP
Inversion of Control (IoC) is a design principle where the control of object creation and its lifecycle is transferred from the application's main routine to an external container or framework. The purpose of IoC is to invert the flow of control compared to traditional control flow. Instead of the application code deciding when and how to create an object, the "container" makes those decisions.
This decoupling allows for more modular code, easier testing, and better separation of concerns. Dependency Injection (DI) is a popular method to achieve IoC, where objects receive their dependencies from an external source rather than creating them internally.
Imagine a classic scenario where you have an application that sends notifications. Without IoC, you might have:
public class EmailService { public void sendEmail(String message, String receiver){ // logic to send email } } public class Notification { private EmailService emailService = new EmailService(); public void notifyUser(String message, String receiver) { emailService.sendEmail(message, receiver); } }
In the above example, Notification
class directly instantiates EmailService
. This makes it hard to change the notification mechanism in the future (e.g., switch to SMS or a messaging app).
Now, let's implement this with IoC using DI:
public interface MessageService { void sendMessage(String msg, String receiver); } public class EmailServiceImpl implements MessageService { public void sendMessage(String msg, String receiver){ // logic to send email } } public class SMSServiceImpl implements MessageService { public void sendMessage(String msg, String receiver){ // logic to send SMS } } public class Notification { private MessageService service; public Notification(MessageService svc){ this.service = svc; } public void notifyUser(String message, String receiver) { service.sendMessage(message, receiver); } }
In this refactored code:
MessageService
interface.MessageService
: EmailServiceImpl
(sends emails) and SMSServiceImpl
(sends SMS).Notification
class now takes a MessageService
as a constructor argument (Dependency Injection).When you want to send a notification via email:
MessageService emailService = new EmailServiceImpl(); Notification notification = new Notification(emailService); notification.notifyUser("Hello", "user@example.com");
Or if you want to send a notification via SMS:
MessageService smsService = new SMSServiceImpl(); Notification notification = new Notification(smsService); notification.notifyUser("Hello", "1234567890");
With IoC in place, the Notification
class doesn't need to be changed if you introduce a new method of sending messages. All you'd need is a new implementation of MessageService
.
In real-world applications, frameworks like Spring or Java EE provide the IoC container, and you don't manually instantiate the classes and inject dependencies like in the above example. Instead, the container manages object creation, lifecycle, and dependency injection.
Inversion of Control (IoC) is a design principle where the control flow of a program is inverted. Instead of the developer controlling the flow, a framework or container takes control. This enhances modularity and flexibility.
// Example without IoC public class WithoutIoC { public static void main(String[] args) { Service service = new Service(); service.doSomething(); } }
// Example with IoC public class WithIoC { private Service service; public WithIoC(Service service) { this.service = service; } public void execute() { service.doSomething(); } }
IoC is implemented through design patterns like Dependency Injection. It helps in creating loosely coupled components.
// Dependency Injection example public class Client { private Service service; // Constructor Injection public Client(Service service) { this.service = service; } public void execute() { service.doSomething(); } }
IoC containers manage object creation, instantiation, and dependency injection. In Java, frameworks like Spring provide IoC containers.
// Spring IoC container example ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Client client = context.getBean(Client.class); client.execute();
IoC is a broader concept, while Dependency Injection is a specific implementation of IoC. IoC focuses on inverting the control flow, while DI deals with how components get their dependencies.
// IoC example public class IoCExample { private Service service; public IoCExample(Service service) { this.service = service; } public void execute() { service.doSomething(); } }
// Dependency Injection example public class DIExample { private Service service; @Autowired public void setService(Service service) { this.service = service; } public void execute() { service.doSomething(); } }
Spring IoC container manages the lifecycle of objects and injects dependencies.
<!-- beans.xml --> <beans> <bean id="service" class="com.example.ServiceImpl"/> <bean id="client" class="com.example.Client"> <property name="service" ref="service"/> </bean> </beans>
IoC is a crucial concept in modern software development, promoting modular, maintainable, and testable code.
// Modern IoC example with Spring Boot @Service public class ModernService { public void doSomething() { // Implementation } }
// Modern IoC usage in a Spring Boot controller @RestController public class ModernController { @Autowired private ModernService service; @GetMapping("/doSomething") public void handleRequest() { service.doSomething(); }