#10 Dependency Injection and Inversion of Control

#10 Dependency Injection and Inversion of Control

Dependency Injection (DI) and Inversion of Control (IoC) are core principles of modern software development, especially in frameworks like Spring. These concepts promote loose coupling and enhance the testability and maintainability of applications. This article will explain the basics of Dependency Injection, how to use Spring Boot for DI, and the various bean scopes and lifecycle in Spring.

Understanding Dependency Injection (DI)

Dependency Injection is a design pattern used to implement IoC, where the control of creating objects is transferred from the program itself to an external entity. Instead of an object creating its dependencies, they are provided to the object, typically through constructors, setters, or method parameters.

Benefits of DI:

  1. Loose Coupling: Reduces dependencies between components, making them easier to manage and test.
  2. Reusability: Components can be reused more easily as they are not tightly bound to specific implementations.
  3. Ease of Testing: Dependencies can be mocked or stubbed during unit testing.

Example:

public class Service {
    private final Repository repository;

    // Constructor Injection
    public Service(Repository repository) {
        this.repository = repository;
    }

    public void performOperation() {
        repository.save();
    }
}

public class Repository {
    public void save() {
        // Save logic
    }
}

Using Spring Boot for DI

Spring Boot simplifies DI through its comprehensive IoC container. Spring manages the lifecycle of beans, injecting dependencies automatically.

Steps to Implement DI in Spring Boot:

  1. Define Components:
    • Annotate classes with @Component, @Service, @Repository, or @Controller to define them as Spring-managed beans.
    Example:
@Service
public class MyService {
    private final MyRepository myRepository;

    // Constructor Injection
    public MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }

    public void doSomething() {
        myRepository.save();
    }
}

@Repository
public class MyRepository {
    public void save() {
        // Save logic
    }
}

2. Enable Component Scanning:

  • Ensure component scanning is enabled, typically through the @SpringBootApplication annotation on the main class.

Example:

@SpringBootApplication
public class MySpringBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApplication.class, args);
    }
}

3. Field Injection (Alternative to Constructor Injection):

  • Use @Autowired to inject dependencies directly into fields.

Example:

@Service
public class MyService {
    @Autowired
    private MyRepository myRepository;

    public void doSomething() {
        myRepository.save();
    }
}

4. Configuration Classes:

  • Use @Configuration classes to define beans explicitly if needed.

Example:

@Configuration
public class AppConfig {
    @Bean
    public MyService myService(MyRepository myRepository) {
        return new MyService(myRepository);
    }

    @Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }
}

Bean Scopes and Lifecycle

Spring defines several bean scopes that determine the lifecycle and visibility of beans within the application context.

Common Bean Scopes:

  1. Singleton (Default):
    • A single instance per Spring IoC container.
    • Created at container startup and shared across the application.
    Example:
@Service
@Scope("singleton")
public class SingletonService {
    // Singleton scoped bean
}

2. Prototype:

  • A new instance is created each time the bean is requested.
  • Useful for stateful beans.

Example:

@Service
@Scope("prototype")
public class PrototypeService {
    // Prototype scoped bean
}

3. Request (Web-aware scopes):

  • A single instance per HTTP request.
  • Used in web applications for request-scoped beans.

Example:

@Component
@Scope("request")
public class RequestScopedBean {
    // Request scoped bean
}

4. Session (Web-aware scopes):

  • A single instance per HTTP session.
  • Useful for session-scoped beans.

Example:

@Component
@Scope("session")
public class SessionScopedBean {
    // Session scoped bean
}

5. Application (Web-aware scopes):

  • A single instance per ServletContext.
  • Useful for application-scoped beans.

Example:

@Component
@Scope("application")
public class ApplicationScopedBean {
    // Application scoped bean
}

Bean Lifecycle Callbacks:

  • InitializingBean and DisposableBean Interfaces:
    • Implement afterPropertiesSet() and destroy() methods for custom initialization and destruction logic.
    Example:
@Component
public class CustomBean implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() {
        // Initialization logic
    }

    @Override
    public void destroy() {
        // Destruction logic
    }
}

@PostConstruct and @PreDestroy Annotations:

  • Annotate methods with @PostConstruct for initialization and @PreDestroy for destruction.

Example:

@Component
public class AnnotatedBean {
    @PostConstruct
    public void init() {
        // Initialization logic
    }

    @PreDestroy
    public void cleanup() {
        // Destruction logic
    }
}

Conclusion

Dependency Injection and Inversion of Control are fundamental concepts that enhance the modularity and maintainability of applications. Spring Boot provides a powerful and flexible DI framework that simplifies the creation and management of dependencies. By understanding and utilizing different bean scopes and lifecycle callbacks, developers can build robust, scalable, and efficient Java applications.

#SpringBoot #DependencyInjection #InversionOfControl #Java #JavaDevelopment #SpringFramework #SoftwareDevelopment #Programming #Coding #JavaProgramming #BeanScopes #DI #IoC #SpringBeans #SpringDI #LearnJava

Leave a Reply