Popular Searches
Popular Course Categories
Popular Courses

Implementing Dependency Injection (DI)

Implementing Dependency Injection (DI)

Spring Framework and Dependency Injection

Implementing Dependency Injection (DI) in Spring Framework

Course: Advance Java Training

Topic: Implementing Dependency Injection (DI)

Course Link: Advance Java Training Course – JustAcademy

Demo Link: Register for a Course Demo

Website: JustAcademy Official Website


1. Introduction to Dependency Injection

Dependency Injection (DI) is a fundamental concept in the Spring Framework that helps developers build loosely coupled, maintainable, reusable, and testable Java applications. It allows an object to receive the other objects it needs instead of creating those objects directly inside its own implementation.

In a traditional Java application, a class may create its dependencies using the new keyword. This approach can tightly couple the class to a specific implementation. With Spring Dependency Injection, the Spring IoC container creates and manages configured objects and supplies their dependencies when constructing or configuring beans.

For example, an OrderService may require a PaymentService to process payments. Instead of creating a new payment service inside the order service, Spring can inject the required payment service automatically.

Learning Objectives

  • Understand the meaning and purpose of Dependency Injection.
  • Differentiate Dependency Injection from Inversion of Control.
  • Understand dependencies, beans, and the Spring IoC container.
  • Implement constructor, setter, and field injection.
  • Use annotations such as @Component, @Service, and @Autowired.
  • Configure dependency injection using Java configuration.
  • Understand XML-based dependency injection.
  • Resolve multiple bean candidates using @Qualifier and @Primary.
  • Apply dependency injection in practical Spring applications.
  • Test Spring components using injected dependencies and mock implementations.

2. What Is a Dependency?

A dependency is an object or service that another class needs to perform its work. Dependencies may include services, repositories, data access objects, configuration components, or external integration clients.

Consider an application that sends notifications to users. A notification manager depends on a notification service to deliver a message.

Example

public class NotificationService {
    public void sendNotification() {
        System.out.println("Notification sent successfully");
    }
}

public class UserManager {
    private NotificationService notificationService;

    public UserManager() {
        notificationService = new NotificationService();
    }

    public void notifyUser() {
        notificationService.sendNotification();
    }
}

In this example, UserManager directly creates the NotificationService object. This means the manager controls both its primary responsibility and the creation of its dependency.

Problems with Direct Dependency Creation

  • Tight coupling: The class depends directly on a particular implementation.
  • Difficult testing: Replacing the dependency with a mock or test implementation becomes harder.
  • Limited flexibility: Changing implementations may require modifying the dependent class.
  • Repeated object creation: Objects may be created unnecessarily when shared or centrally managed instances would be more suitable.
  • Reduced maintainability: Object creation and business logic become mixed together.

3. What Is Dependency Injection in Spring?

Dependency Injection is a design technique in which an object receives its dependencies from an external source. In Spring, the IoC container usually performs this task for Spring-managed beans.

The dependent class declares what it needs, and Spring resolves the appropriate bean and provides it through a constructor, setter method, or another supported injection point.

Traditional Approach vs. Dependency Injection

Traditional Java Spring Dependency Injection
The class often creates its dependencies directly. The container supplies configured dependencies.
Implementations may be tightly coupled. Dependencies can be defined using interfaces and interchangeable implementations.
Replacing dependencies may require code changes. Implementations can often be changed through configuration.
Unit testing may require additional setup. Mock or fake dependencies can be supplied more easily.
Object creation logic is distributed across classes. Object creation and configuration can be managed centrally.

Example of Spring Dependency Injection

import org.springframework.stereotype.Component;

@Component
public class NotificationService {

    public void sendNotification() {
        System.out.println("Notification sent successfully");
    }
}
import org.springframework.stereotype.Service;

@Service
public class UserManager {

    private final NotificationService notificationService;

    public UserManager(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    public void notifyUser() {
        notificationService.sendNotification();
    }
}

Here, @Component and @Service mark the classes for component scanning. When the classes are discovered and registered as beans, Spring can create the NotificationService and inject it into the UserManager constructor.


4. Relationship Between IoC and DI

Inversion of Control (IoC) is a broad design principle in which control over some aspect of a program is transferred from application code to a framework or container. Dependency Injection is one of the common ways to implement IoC.

Spring's IoC container is responsible for creating and managing beans, resolving their dependencies, and applying relevant configuration.

How IoC and DI Work Together

  1. The developer defines application components and their dependencies.
  2. Spring discovers or registers the bean definitions.
  3. The container determines which dependencies are required.
  4. Spring creates and configures the relevant objects.
  5. The required dependencies are injected into the dependent beans.
  6. The application uses the fully configured beans.

IoC vs. DI

Inversion of Control (IoC) Dependency Injection (DI)
A general design principle. A technique used to implement IoC.
Transfers responsibility for selected operations to a framework or container. Supplies required dependencies to an object.
In Spring, the container manages bean creation and configuration. Spring injects dependencies through constructors, setters, or supported injection points.

5. Types of Dependency Injection in Spring

Spring supports several dependency injection approaches. The most commonly discussed types are constructor injection, setter injection, and field injection.

5.1 Constructor Injection

Constructor injection supplies dependencies through the class constructor. Spring invokes the constructor with the appropriate bean instances when creating the dependent bean.

Constructor injection is generally recommended for required dependencies because it makes the class's requirements explicit and allows required fields to be declared final.

Example

import org.springframework.stereotype.Component;

@Component
public class EmailService {

    public void sendEmail() {
        System.out.println("Email sent successfully");
    }
}
import org.springframework.stereotype.Service;

@Service
public class CustomerService {

    private final EmailService emailService;

    public CustomerService(EmailService emailService) {
        this.emailService = emailService;
    }

    public void registerCustomer() {
        System.out.println("Customer registered");
        emailService.sendEmail();
    }
}

When Spring manages both classes and discovers their bean definitions, it injects the EmailService into CustomerService.

Advantages of Constructor Injection

  • Required dependencies are visible in the constructor.
  • Supports immutable dependency fields through final.
  • Ensures the object receives its required dependencies during construction.
  • Makes unit testing straightforward.
  • Helps identify classes with too many responsibilities when constructors become excessively large.

5.2 Setter Injection

Setter injection supplies a dependency through a setter method after the object has been constructed. It is useful when a dependency is optional or can reasonably be configured after construction.

Example

import org.springframework.stereotype.Component;

@Component
public class PdfExporter {

    public void export() {
        System.out.println("Report exported as PDF");
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ReportService {

    private PdfExporter pdfExporter;

    @Autowired
    public void setPdfExporter(PdfExporter pdfExporter) {
        this.pdfExporter = pdfExporter;
    }

    public void generateReport() {
        if (pdfExporter != null) {
            pdfExporter.export();
        }
    }
}

Spring calls the setter when it configures the bean. The null check in this example also permits the service to operate without an exporter if the application intentionally allows that behavior. In a real application, optional dependency behavior should be clearly defined.

Advantages of Setter Injection

  • Useful for optional dependencies.
  • Allows configuration after the object has been constructed.
  • Can be appropriate for legacy classes or frameworks that require setter-based configuration.
  • Makes it possible to provide a default behavior when an optional dependency is absent.

5.3 Field Injection

Field injection uses an annotation such as @Autowired directly on a field. Spring injects the dependency into the field when it configures the bean.

Example

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BillingService {

    @Autowired
    private EmailService emailService;

    public void sendBillingNotification() {
        emailService.sendEmail();
    }
}

Limitations of Field Injection

  • Dependencies are less visible from the class constructor.
  • Fields cannot be declared final for ordinary field injection.
  • Testing the class in isolation may require reflection or container setup unless additional design changes are made.
  • It can make the class's required dependencies less explicit.

Although Spring supports field injection, constructor injection is generally a better default for mandatory dependencies.

Comparison of Injection Types

Feature Constructor Injection Setter Injection Field Injection
Injection mechanism Constructor arguments Setter methods Direct field assignment
Required dependencies Excellent choice Possible, with validation Supported, but less explicit
Optional dependencies Can use explicit optional types or alternative constructors Often suitable Possible, but less explicit
Immutable fields Supports final fields Usually requires a mutable field Ordinary field injection does not support a final field
Unit testing Usually straightforward Usually straightforward May require additional setup
General recommendation Preferred for required dependencies Useful for optional dependencies Generally less preferred for application code

6. Understanding the @Autowired Annotation

The @Autowired annotation tells Spring to resolve a dependency from its bean container and inject it at a supported injection point.

It can be used on constructors, fields, and methods. However, if a Spring-managed class has only one constructor, Spring can use that constructor for injection without requiring @Autowired.

Example with Constructor Injection

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class OrderService {

    private final PaymentService paymentService;

    @Autowired
    public OrderService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }
}

In this example, the annotation is optional if this is the class's only constructor.

Example with Method Injection

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class NotificationManager {

    private NotificationService notificationService;

    @Autowired
    public void configureNotificationService(
            NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}

Spring resolves the method parameter and supplies the matching bean when configuring the component.

Important Points About @Autowired

  • Spring must manage the target object for annotation-based injection to occur automatically.
  • The dependency must be available as a compatible bean or be supplied through supported configuration.
  • If multiple beans match a dependency type, Spring needs a way to determine which one to use.
  • Constructor injection is usually clearer for required dependencies.

7. Using @Component, @Service, @Repository, and @Controller

Spring provides stereotype annotations that identify application classes as candidates for component scanning. They help organize an application according to the responsibilities of its components.

Annotation Purpose Typical Example
@Component General-purpose Spring-managed component. Utility or notification component
@Service Identifies a service or business-logic component. OrderService
@Repository Identifies a persistence or data access component. CustomerRepository
@Controller Identifies a Spring MVC controller. CustomerController
@RestController Combines controller semantics with response-body handling. REST API controller

Example of a Layered Application

import org.springframework.stereotype.Repository;

@Repository
public class ProductRepository {

    public String findProductName() {
        return "Laptop";
    }
}
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public String getProductName() {
        return productRepository.findProductName();
    }
}

The repository provides data access, while the service uses the repository to perform an application-level operation. Spring injects the repository into the service when both are registered as beans.


8. Implementing Dependency Injection Using Java Configuration

Java-based configuration uses @Configuration and @Bean to define objects managed by Spring. This approach is useful when configuring third-party classes, creating beans explicitly, or defining application wiring in a configuration class.

Step 1: Create a Service Class

public class MessageService {

    public String getMessage() {
        return "Dependency Injection using Java Configuration";
    }
}

Step 2: Create a Configuration Class

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public MessageService messageService() {
        return new MessageService();
    }

    @Bean
    public MessagePrinter messagePrinter(
            MessageService messageService) {
        return new MessagePrinter(messageService);
    }
}

Step 3: Create the Dependent Class

public class MessagePrinter {

    private final MessageService messageService;

    public MessagePrinter(MessageService messageService) {
        this.messageService = messageService;
    }

    public void printMessage() {
        System.out.println(messageService.getMessage());
    }
}

Step 4: Load the Application Context

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApplication {

    public static void main(String[] args) {
        try (AnnotationConfigApplicationContext context =
                 new AnnotationConfigApplicationContext(
                     AppConfig.class)) {

            MessagePrinter printer =
                context.getBean(MessagePrinter.class);

            printer.printMessage();
        }
    }
}

Expected Output

Dependency Injection using Java Configuration

Explanation

  1. AppConfig declares two Spring beans.
  2. The messageService() method defines the message service bean.
  3. The messagePrinter() method accepts the message service as a method parameter.
  4. Spring resolves that parameter and supplies the managed service bean.
  5. The application retrieves the configured printer from the context and invokes its method.

9. Implementing Dependency Injection Using XML Configuration

XML-based configuration is another way to define Spring beans and their dependencies. It remains useful when maintaining older applications or when bean configuration needs to be kept outside Java classes.

Step 1: Create the Java Classes

public class GreetingService {

    public void greet() {
        System.out.println("Welcome to Spring DI");
    }
}
public class GreetingManager {

    private GreetingService greetingService;

    public void setGreetingService(
            GreetingService greetingService) {
        this.greetingService = greetingService;
    }

    public void execute() {
        greetingService.greet();
    }
}

Step 2: Define the Beans in XML

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="greetingService"
          class="com.example.GreetingService"/>

    <bean id="greetingManager"
          class="com.example.GreetingManager">
        <property name="greetingService"
                  ref="greetingService"/>
    </bean>

</beans>

Replace the example package names with the actual fully qualified class names used in the project.

Step 3: Load the XML Configuration

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApplication {

    public static void main(String[] args) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext(
                "applicationContext.xml"
            );

        GreetingManager manager =
            context.getBean(
                "greetingManager",
                GreetingManager.class
            );

        manager.execute();
    }
}

Expected Output

Welcome to Spring DI

Spring creates the configured beans, calls the setter on GreetingManager, and supplies the GreetingService dependency.


10. Resolving Multiple Beans with @Qualifier

Sometimes an application contains multiple beans of the same interface. If a dependent class requests that interface, Spring may not know which implementation should be injected.

Example Interface

public interface MessageSender {
    void send(String message);
}

First Implementation

import org.springframework.stereotype.Component;

@Component("emailSender")
public class EmailSender implements MessageSender {

    @Override
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

Second Implementation

import org.springframework.stereotype.Component;

@Component("smsSender")
public class SmsSender implements MessageSender {

    @Override
    public void send(String message) {
        System.out.println("SMS: " + message);
    }
}

Injecting a Specific Implementation

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class AlertService {

    private final MessageSender messageSender;

    public AlertService(
            @Qualifier("emailSender")
            MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public void sendAlert() {
        messageSender.send("Your order has been confirmed");
    }
}

The @Qualifier("emailSender") annotation narrows the candidate selection to the bean identified by that qualifier. In this example, the email sender is selected.

Using @Primary

The @Primary annotation identifies a preferred bean when multiple candidates of the same type are available and no more specific qualifier overrides the choice.

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class EmailSender implements MessageSender {

    @Override
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

Use @Qualifier when a specific implementation is needed at an injection point and @Primary when one implementation should generally be preferred.


11. Optional Dependencies and Dependency Injection

Not every dependency is mandatory. Some components may provide additional functionality that is available only when a particular bean is configured.

Spring supports optional dependency patterns, including Optional<T>, nullable dependencies where appropriately configured, and optional setter injection.

Example Using Optional

import java.util.Optional;
import org.springframework.stereotype.Service;

@Service
public class CustomerService {

    private final CustomerRepository customerRepository;

    public CustomerService(
            Optional<CustomerRepository> customerRepository) {
        this.customerRepository = customerRepository.orElse(null);
    }

    public void displayStatus() {
        if (customerRepository != null) {
            System.out.println("Repository is available");
        } else {
            System.out.println("Repository is not configured");
        }
    }
}

This pattern is useful only when the application genuinely supports operating without the repository. A repository required for the service's core function should normally be injected as a mandatory dependency instead.

Best Practices for Optional Dependencies

  • Use optional injection only when the dependency is truly optional.
  • Define a clear fallback behavior.
  • Avoid converting missing required dependencies into silent null values.
  • Prefer explicit configuration over complicated conditional dependency handling.

12. Dependency Injection with Interfaces

Using interfaces for dependencies makes implementations easier to replace and helps reduce coupling. A service can depend on an abstraction rather than a particular class.

Step 1: Define an Interface

public interface PaymentProcessor {
    void processPayment(double amount);
}

Step 2: Create an Implementation

import org.springframework.stereotype.Component;

@Component
public class CardPaymentProcessor
        implements PaymentProcessor {

    @Override
    public void processPayment(double amount) {
        System.out.println(
            "Card payment processed: " + amount
        );
    }
}

Step 3: Inject the Interface

import org.springframework.stereotype.Service;

@Service
public class CheckoutService {

    private final PaymentProcessor paymentProcessor;

    public CheckoutService(PaymentProcessor paymentProcessor) {
        this.paymentProcessor = paymentProcessor;
    }

    public void checkout(double amount) {
        paymentProcessor.processPayment(amount);
    }
}

The CheckoutService does not need to know how the payment is processed. Another implementation can be supplied through configuration, provided Spring can resolve the intended bean.

Benefits of Interface-Based DI

  • Supports interchangeable implementations.
  • Improves unit testing through mock implementations.
  • Reduces dependencies on implementation details.
  • Encourages clear component contracts.
  • Supports cleaner application architecture.

13. Dependency Injection in a Spring Boot Application

Spring Boot simplifies application setup, but dependency injection is still provided by the Spring Framework. Spring Boot applications commonly use component scanning, stereotype annotations, constructor injection, and configuration classes.

Example: Product Management

Product Repository

import org.springframework.stereotype.Repository;

@Repository
public class ProductRepository {

    public String getProductName() {
        return "Wireless Headphones";
    }
}

Product Service

import org.springframework.stereotype.Service;

@Service
public class ProductService {

    private final ProductRepository productRepository;

    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    public String getProduct() {
        return productRepository.getProductName();
    }
}

Product Controller

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {

    private final ProductService productService;

    public ProductController(ProductService productService) {
        this.productService = productService;
    }

    @GetMapping("/product")
    public String getProduct() {
        return productService.getProduct();
    }
}

Application Class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

Execution Flow

  1. Spring Boot starts the application context.
  2. Component scanning discovers the controller, service, and repository.
  3. The IoC container creates and manages the registered beans.
  4. The repository is injected into the service.
  5. The service is injected into the controller.
  6. A request to /product invokes the controller method.
  7. The controller calls the service, which retrieves the product name from the repository.
  8. The response is returned to the client.

For this example, the repository returns a hard-coded value. In a database-backed application, the repository would access a configured database or persistence layer.


14. Dependency Injection and Unit Testing

One of the most valuable benefits of Dependency Injection is easier testing. A class that receives its dependencies through a constructor can be instantiated directly with real, fake, or mock implementations.

Example Service

public interface GreetingProvider {
    String getGreeting();
}
public class GreetingManager {

    private final GreetingProvider greetingProvider;

    public GreetingManager(GreetingProvider greetingProvider) {
        this.greetingProvider = greetingProvider;
    }

    public String greet() {
        return greetingProvider.getGreeting();
    }
}

Simple Unit Test Without Starting Spring

public class GreetingManagerTest {

    public static void main(String[] args) {
        GreetingProvider fakeProvider =
            () -> "Hello from test";

        GreetingManager manager =
            new GreetingManager(fakeProvider);

        System.out.println(manager.greet());
    }
}

Expected Output

Hello from test

This example uses a small fake implementation and does not require a Spring application context. In a real project, a testing framework such as JUnit can be used to make assertions and automate test execution.

Testing Benefits

  • Dependencies can be replaced without modifying the production class.
  • Unit tests can focus on business logic.
  • Tests can run without connecting to external services when fakes or mocks are appropriate.
  • Components become easier to reuse in different contexts.

15. Common Dependency Injection Errors

15.1 No Qualifying Bean Found

Cause: Spring cannot find a suitable bean for a required dependency.

Solution: Verify that the implementation is registered as a bean, that component scanning covers its package, or that a suitable @Bean definition exists.

15.2 Multiple Beans of the Same Type

Cause: Several beans match the required dependency type, so Spring cannot determine which one to inject.

Solution: Use @Qualifier, @Primary, or explicit configuration to select the intended implementation.

15.3 Circular Dependency

Cause: Two or more beans depend on one another in a cycle.

Solution: Refactor responsibilities to remove the cycle. Constructor-injected circular dependencies generally fail during bean creation. Switching to setter injection is not a preferred general solution.

15.4 Missing Component Scanning

Cause: A class has a stereotype annotation but is outside the configured component-scanning packages.

Solution: Place application components under the appropriate root package or configure scanning explicitly.

15.5 Attempting Injection into an Unmanaged Object

Cause: The application creates an object manually with new and expects Spring annotations to inject its dependencies.

Solution: Retrieve the bean from the Spring container or define the object through appropriate Spring configuration.

15.6 Too Many Constructor Dependencies

Cause: A class requires a large number of unrelated dependencies.

Solution: Review the class's responsibilities and consider separating unrelated functionality into focused components.


16. Best Practices for Implementing Dependency Injection

  • Prefer constructor injection for mandatory dependencies.
  • Use final fields for required constructor-injected dependencies where practical.
  • Use setter injection when a dependency is genuinely optional or requires post-construction configuration.
  • Avoid relying on field injection as the default for application services.
  • Depend on interfaces when they provide a useful abstraction.
  • Keep business logic separate from object creation and infrastructure setup.
  • Use @Service, @Repository, and @Controller to communicate component responsibilities clearly.
  • Use @Qualifier when a specific implementation must be injected.
  • Use @Primary for an appropriate default among multiple candidates.
  • Do not create circular dependencies between services.
  • Keep singleton beans free from unnecessary mutable, request-specific state.
  • Write unit tests that supply fake or mock dependencies.
  • Use explicit Java configuration for third-party classes that cannot be annotated.
  • Do not expect dependency injection to work automatically on arbitrary objects created outside the Spring container.
  • Keep configuration consistent and verify that component scanning covers the intended packages.

17. Practice Exercises

Exercise 1: Basic Constructor Injection

Create a StudentService class and a StudentManager class. Inject the service into the manager using constructor injection and display a student message.

Exercise 2: Setter Injection

Create a ReportExporter component and inject it into a ReportService using a setter method. Execute the export method.

Exercise 3: Interface-Based Injection

Create a NotificationSender interface with email and SMS implementations. Inject the required implementation into a notification manager.

Exercise 4: Resolve Multiple Bean Candidates

Register two implementations of the same interface. Use @Qualifier to inject one implementation and then experiment with @Primary.

Exercise 5: Java Configuration

Define a service and a manager using @Configuration and @Bean. Retrieve the manager from an ApplicationContext.

Exercise 6: XML Configuration

Configure a service bean and inject it into another class through XML constructor arguments or property references.

Exercise 7: Spring Boot REST API

Create a controller, service, and repository for a simple product application. Use constructor injection to connect the layers.

Exercise 8: Unit Testing

Create a service that depends on an interface. Supply a fake implementation in a unit test and verify the service's output without starting Spring.


18. Interview Questions and Answers

Q1. What is Dependency Injection?

Answer: Dependency Injection is a design technique in which an object receives its dependencies from an external source rather than creating or locating them directly. Spring commonly performs this work through its IoC container.

Q2. What is the difference between IoC and DI?

Answer: IoC is the broader principle of transferring control to a framework or container. DI is a technique that supplies dependencies to an object and is commonly used by Spring to implement IoC.

Q3. What are the main types of dependency injection in Spring?

Answer: The commonly discussed types are constructor injection, setter injection, and field injection.

Q4. Which injection method is generally recommended?

Answer: Constructor injection is generally recommended for required dependencies because it makes dependencies explicit, supports immutable fields, and simplifies unit testing.

Q5. What is the purpose of @Autowired?

Answer: It instructs Spring to resolve a suitable dependency and inject it at a supported constructor, field, or method injection point. It is not required on a class's only constructor.

Q6. What is a Spring bean?

Answer: A Spring bean is an object created, configured, or otherwise managed by the Spring IoC container.

Q7. What is the difference between @Component and @Service?

Answer: Both are component-scanning stereotypes. @Component is general-purpose, while @Service communicates that the class represents a service or business-logic component.

Q8. What is the purpose of @Repository?

Answer: It identifies a persistence component and participates in Spring's repository-related exception translation support when appropriately configured.

Q9. What happens when multiple beans match a dependency?

Answer: Spring resolves the candidates using its dependency-resolution rules. If the intended bean cannot be determined, the application may fail to start. @Qualifier or @Primary can help resolve the ambiguity.

Q10. What is the difference between constructor and setter injection?

Answer: Constructor injection supplies dependencies when the object is created and is suitable for required dependencies. Setter injection supplies dependencies after construction and can be useful for optional configuration.

Q11. Why is field injection generally less preferred?

Answer: It makes dependencies less explicit, does not support ordinary final-field injection, and can make isolated unit testing more difficult.

Q12. Can Spring inject dependencies into objects created with new?

Answer: Not automatically in the usual case. Objects created directly by application code are not automatically managed by Spring, so their injection annotations are not processed unless they are deliberately integrated with Spring's object-management facilities.

Q13. What is @Qualifier?

Answer: It narrows dependency resolution to a specific bean candidate or qualifier when multiple implementations are available.

Q14. What is @Primary?

Answer: It marks a bean as a preferred candidate when multiple beans of the same type are available and no more specific selection takes precedence.

Q15. How does DI improve testing?

Answer: DI allows test code to supply mock, fake, or stub dependencies. This makes it easier to test business logic independently of external systems and the Spring container.

Q16. What is circular dependency in Spring?

Answer: It occurs when two or more beans require one another in a dependency cycle. Constructor-based cycles generally prevent the beans from being created successfully and should usually be removed through refactoring.

Q17. Can a Spring application use both constructor and setter injection?

Answer: Yes. A class can receive required dependencies through its constructor and optional dependencies through setter methods when this design is appropriate.

Q18. What is the role of @Bean?

Answer: The @Bean annotation marks a method in a Spring configuration class as a factory method whose returned object is registered as a Spring-managed bean.


19. Quick Revision Checklist

  • Understand the definition and purpose of Dependency Injection.
  • Explain the relationship between IoC and DI.
  • Identify the role of the Spring IoC container.
  • Implement constructor injection for required dependencies.
  • Use setter injection for suitable optional dependencies.
  • Understand the limitations of field injection.
  • Recognize the roles of @Component, @Service, and @Repository.
  • Use @Autowired correctly.
  • Resolve multiple candidates with @Qualifier or @Primary.
  • Configure dependencies with Java configuration and XML.
  • Understand interface-based dependency injection.
  • Recognize common bean-resolution and circular-dependency errors.
  • Apply dependency injection in Spring Boot applications.
  • Write unit tests using fake or mock dependencies.

20. Conclusion

Dependency Injection is one of the central concepts of the Spring Framework. It allows application components to receive the objects they need without managing those dependencies directly. This improves separation of concerns, maintainability, flexibility, and testability.

Spring supports constructor, setter, and field injection, along with Java-based and XML-based bean configuration. Constructor injection is generally the best starting point for required dependencies, while setter injection can be useful for optional configuration. Annotations such as @Component, @Service, @Autowired, @Qualifier, and @Primary help developers define and resolve dependencies effectively.

By applying these concepts to controllers, services, repositories, and external integrations, developers can create well-structured Java applications that are easier to test, extend, and maintain.

Continue Learning with JustAcademy

Explore the Advance Java Training Course to develop your knowledge of Spring, Java enterprise development, database integration, and application architecture.

To learn more about the training program, register for a course demo.

Visit the JustAcademy Official Website to explore additional courses and learning opportunities.

Previous Next
whatsapp