← Back to Home

Module 2: Dependency Injection 1

Module Overview

Introduction to dependency injection and its benefits in Java applications. Learn the foundational concepts and patterns for implementing DI.

Learning Objectives

Video Content: Dependency Injection Motivation

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.

Video Content: Introducing Dagger

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.

Video Content: Dagger @Provides, @Module, and @Singleton

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.

Video Content: Sprint 14 Dependency Injection 1 Overview

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.

Resources