03 September 2023

Spring Dependency Injection - learngreen.net

 



Spring Framework is a dependable companion for developers when it comes to creating reliable and maintainable Java applications. Dependency Injection (DI), one of the many potent features Spring offers, stands out as a basic idea. In order to make Spring Dependency Injection understandable to novices, we will explain it in simple terms and provide brief code samples in this post.


Knowledge of Dependency Injection


Dependency Injection is fundamentally a design pattern and a part of Spring. It allows you to achieve loose connections between various application components. Because it makes your code more modular, manageable, and less prone to problems, loose coupling is essential.


Simply said, Dependency Injection offers a solution to the problem of how an object might obtain its dependencies—that is, the other objects it depends on—instead of producing them on its own. Take a class that requires another class to complete a certain task. You allow someone else to supply it rather than creating an instance of that class inside your class. The Spring container is that "someone else" in the Spring universe.


CI Container for Dependency Injection

The components (commonly referred to as beans) in your application, as well as their dependencies, are managed by the Dependency Injection container in Spring, which is frequently referred to as the ApplicationContext. Using the configuration you supply, it generates them and connects them.

 



     public class UserService {
     private UserRepository userRepository;

    // Constructor-based DI
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
       }

       // ...
       }
  

 

 The UserService class in the code above needs a UserRepository to carry out its functions. We use constructor-based Dependency Injection in place of generating an instance of UserRepository within the UserService. In essence, what we're saying is, "Please give me a UserService, and I'll need a UserRepository to go with it."



Let's see how this is configured in Spring.


Configuration with XML

In Spring, you typically configure your beans using XML. Here's a simplified example of how you might configure the above classes:-

<bean id="userRepository" class="com.example.UserRepository"/>

<bean id="userService" class="com.example.UserService">

    <constructor-arg ref="userRepository"/>

</bean>

In this XML configuration, we define two beans: userRepository and userService. The <constructor-arg> element specifies the dependency (userRepository) that should be injected into the userService bean.


Configuration with Java-based Annotation


Alternatively, you can configure your Spring application using Java-based annotations. This approach is more modern and often preferred. Here's how you would do the same 

configuration with annotations:-




   @Configuration
    public class AppConfig {
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }

    @Bean
    public UserService userService() {
        return new UserService(userRepository());
       }
   }

 In this Java-based configuration, we use the @Bean annotation to declare our beans. The userRepository( ) method is annotated as @Bean, indicating that it provides a UserRepository bean. Similarly, the userService( ) method provides a UserService bean and injects the userRepository dependency using the constructor.



Types of Dependency Injection

Spring supports several ways to perform Dependency Injection:


Constructor Injection: As shown in our examples, this is the most common and recommended form of DI. You inject dependencies through a class's constructor.


Setter Injection: Instead of using a constructor, you provide setter methods for your dependencies. Spring sets these dependencies through the setters. Here's an example:



    public class UserService {
    private UserRepository userRepository;

    // Setter-based DI
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
        }

    // ...
   }

 In XML configuration, you'd use <property> elements to inject the dependency, and in Java-based configuration, you'd use @Autowired or @Inject annotations on the setter method.



Field Injection: This method involves directly injecting dependencies into class fields. Although it's the simplest to implement, it's considered less robust and should be used sparingly. Here's an example:-



  public class UserService {
    @Autowired
    private UserRepository userRepository;

    // ...
     }
 

 

 Method Injection:- Similar to setter injection, you can inject dependencies using methods other than constructors. This is less common and is used in specific scenarios.


Dependency Injection's benefits

Let's examine Dependency Injection's advantages now that we know what it is:-


DI encourages modularity by enabling you to swap out or update components without having an impact on the rest of your program. For long-term maintainability, this is crucial.


Testability: Using DI makes it simple to switch real implementations out for mock objects while doing tests. Your unit tests become more isolated and trustworthy as a result.


Readability: Because it explicitly declares its dependencies, code that uses DI is frequently more readable. This can make it easier for fresh engineers to understand your codebase.


Reusability: Components with definite dependencies are frequently more transferable between various portions of your application.


Dependency Injection scopes

When using Spring beans, it's crucial to take into account their scope, which establishes their lifespan and the manner in which they are distributed within the application environment. These are the most typical bean scopes:


Singleton: The default scope is this one. The bean is formed in a single instance and shared by the entire application environment.


Every time a bean is requested, a new instance of the bean is constructed as a prototype.


Every HTTP request results in the creation of a new instance (for web applications).


For each HTTP session, a new instance is created (for web applications).


Depending on the needs of your application, you should choose the appropriate scope for your beans. For services that should be shared globally, such as a database connection pool, you might utilize a singleton scope. 


Here's how you can specify a bean's scope in Spring:-


In XML configuration:-

<bean id="userService" class="com.example.UserService" scope="singleton"/>

In Java-based configuration:-



  @Bean
  @Scope("prototype")
  public UserService userService() {
    return new UserService(userRepository());
   }

 Using Annotations for Dependency Injection

While XML configuration is still widely used, Spring offers a more concise way to enable Dependency Injection using annotations. Annotations make your code cleaner and more readable.


Here are some commonly used annotations for Dependency Injection:


@Autowired:-  This annotation can be used on fields, constructors, setter methods, or even methods. Spring will automatically inject the required dependencies by type. For example:-



  @Component
   public class UserService {
    @Autowired
    private UserRepository userRepository;
    
        // ...
     }
  

 @Qualifier: When you have multiple beans of the same type, you can use @Qualifier to specify which one should be injected. This is often used in conjunction with @Autowired. For example:-



    @Component
    public class UserService {
    @Autowired
    @Qualifier("userRepository")
    private UserRepository userRepository;
    
      // ...
   }
 

 @Resource: This annotation is similar to @Autowired but provides more fine-grained control over the injection. It allows you to specify the name of the bean to inject. For example:-



   @Component
    public class UserService {
    @Resource(name = "userRepository")
    private UserRepository userRepository;
    
      // ...
  }

 Common Errors and Recommended Practices

Although the idea of dependency injection is strong, there are some typical mistakes to avoid:


Beware of cyclic dependencies, in which two or more beans are dependent on one another. Although Spring offers solutions for this, it's ideal to build your application so that circular dependencies are avoided from the start.


Overuse of Singleton Scope: Singletons are useful, but they shouldn't be used for everything. Only use singleton scope for items that actually require global sharing.


Overusing field injection might reduce your code's testability and dependencies' explicitness. In general, constructor or setter injection is preferable.


Using XML Configuration: Although XML configuration is still viable, many developers like Java-based configuration or annotation-driven DI since they are shorter and simpler to read.


Conclusion

The Spring Framework's core idea of dependency injection encourages loose coupling between components, which makes your code more modular, maintainable, and testable. Spring offers a number of methods for implementing Dependency Injection, including Java-based annotations and XML configuration. You may develop cleaner, more maintainable code and create systems that are simpler to test and extend by knowing the guiding principles and best practices of Dependency Injection.


No comments:

Post a Comment