Introduction to dependency injection and its benefits in Java applications. Learn the foundational concepts and patterns for implementing DI.
This video explains why dependency injection is important and how it solves problems with tightly coupled code. Understanding the motivation behind DI helps appreciate its value in software design.
// Without dependency injection - tight coupling public class UserService { // Direct instantiation creates tight coupling private UserRepository userRepository = new MySQLUserRepository(); public User getUserById(String id) { return userRepository.findById(id); } } // With dependency injection - loose coupling public class UserService { // Dependency is injected, not created internally private final UserRepository userRepository; // Constructor injection public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User getUserById(String id) { return userRepository.findById(id); } }
This example contrasts two approaches: one with hardcoded dependencies (tight coupling) and another with injected dependencies (loose coupling). The second approach is more flexible, testable, and maintainable because the UserService doesn't need to know the concrete implementation of UserRepository.
This video introduces Dagger, a popular dependency injection framework for Java and Android applications. Dagger uses code generation to create an efficient DI implementation at compile time.
// Define a Dagger component interface @Component public interface ApplicationComponent { // Methods to get dependencies UserService userService(); // Method to inject dependencies into an object void inject(MainActivity activity); } // Using the component public class MyApplication { private ApplicationComponent component; public void onCreate() { // Dagger will generate this implementation component = DaggerApplicationComponent.create(); // Get a dependency UserService userService = component.userService(); } }
This code illustrates the basic structure of Dagger usage. The @Component annotation defines the interface between the dependency consumer and provider. Dagger generates an implementation of this interface that handles the creation and provision of the dependencies.
This video explores Dagger's core annotations for configuring dependency injection. @Provides methods define how to create dependencies, @Module groups these methods, and @Singleton ensures only one instance is created.
// Dagger module with provider methods @Module public class AppModule { // Provides a singleton database connection @Provides @Singleton public Database provideDatabase() { return new PostgresDatabase("jdbc:postgresql://localhost/mydb"); } // Provides a UserRepository that depends on Database @Provides public UserRepository provideUserRepository(Database database) { return new SQLUserRepository(database); } // Provides UserService that depends on UserRepository @Provides public UserService provideUserService(UserRepository repository) { return new UserServiceImpl(repository); } } // Component with module @Singleton @Component(modules = {AppModule.class}) public interface AppComponent { UserService userService(); }
This example shows a Dagger module with provider methods. The @Singleton annotation on the Database provider ensures only one instance is created and reused. The dependency graph is clear: UserService depends on UserRepository, which depends on Database. Dagger manages this graph automatically.
This video provides a comprehensive overview of the Dependency Injection concepts covered in this module, bringing together all the key points from previous videos.
// Practical application - full Dagger setup // Service layer with constructor injection public class OrderService { private final OrderRepository orderRepository; private final PaymentService paymentService; private final NotificationService notificationService; @Inject // Tells Dagger to inject dependencies public OrderService(OrderRepository orderRepository, PaymentService paymentService, NotificationService notificationService) { this.orderRepository = orderRepository; this.paymentService = paymentService; this.notificationService = notificationService; } public void processOrder(Order order) { orderRepository.save(order); paymentService.processPayment(order.getPayment()); notificationService.sendOrderConfirmation(order); } } // Component that brings everything together @Singleton @Component(modules = {RepositoryModule.class, ServiceModule.class}) public interface ApplicationComponent { OrderService orderService(); }
This comprehensive example demonstrates a complete Dagger setup with constructor injection using @Inject. The OrderService has multiple dependencies that are automatically provided by Dagger. The component interface combines multiple modules to form the complete dependency graph.