Dependency Injection in Spring

Joe • updated : June 10, 2020

1. What is dependency injection and what are the advantages?

In software engineering, traditionally, a custom piece of code controls its call to a reusable code or framework, however, with Inversion of control (IoC), it is the framework that calls into the custom piece of code

Inversion of control (IoC) is sometimes referred to as the "Hollywood Principle: Don't call us, we'll call you".  This implies that the application code does not control the creation of its dependencies.

Designing a Java class or component to be independent of other classes makes it easier to reuse the designed classes as well as testing them independently of other classes.

Dependency Injection is a specific type of IoC and is a process an object defines its dependencies.

In a nutshell, the process of Dependency Injection (DI) enables application code to maintain the independence of a service it depends on.  This independence makes it easier to use a stub or mock implementations to be used in unit tests and thereby making it easier to test the application code.

Some of the advantages of dependency injection can be summarised as the following; 

  • Loose coupling between different parts of the application.
  • Improved testability using stubs or mocks.
  • Increased reusability of different parts of the application code.
  • Makes extending the application easier.
  • Cleaner code that is easier to understand and maintain.

 

Although the process of dependency injection has its drawbacks such as the creation of unnecessary interfaces and increased number of classes in an application, the benefits outweigh the downside.

There are two major variants of dependency injection namely Constructor-based and Setter-based.

2. Constructor-based dependency injection

This is achieved by the container invoking a constructor with a number of arguments, each representing a dependency.

  • Recommended for mandatory dependencies.
  • Enables application components to be implemented as immutable objects.
  • Ensures that requires dependencies are not null.
  • Injected components are always returned to the client (calling) code in a fully initialised stated.
  • A large number of constructor arguments is a bad practice and should be discouraged and such class should be refactored to better address proper separation of concerns.
    private final CommentService commentService;
    private final Stream> blogRepository;

    @Autowired
    public BlogServiceImpl(CommentService commentService, Stream> blogRepository) {
        this.commentService = commentService;
        this.blogRepository = blogRepository;

    }

3 Setter-based Decency Injection

This is also accomplished by the container calling setter methods on the configured beans after invoking a no-argument constructor or a no-argument static factory method to instantiate the beans.

  • Primarily useful for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency.
  • A major benefit is that setter methods make objects of that class amenable to reconfiguration or re-injection later.
    // a setter method so that the Spring container can inject a CommentService
    @Required
    @Override
    public void setCommentService(CommentService commentService) {
        this.commentService = commentService;
    }
    @Override
    public List>> findCommentsByUsername(String username) {
        return commentService.commentsByUserId(username); //using the injected commentService to perform an operation
    }

The example code below demonstrates how the PublicationService can use the injected services (BlogService & CommentService ) to fulfil its responsibilities, in this instance; finding all published articles and comments by a user. 

4. Unit Test

    /**
     * Test of findPublishedArticles method, of class PublicationService.
     */
    @Test
    public void testFindPublishedArticles() {
        log.debug("testFindPublishedArticles");
        List> result = publicationService.findPublishedArticles();
        assertThat(result).isNotEmpty();
        assertThat(result, hasSize(greaterThanOrEqualTo(3)));

    }

    /**
     * Test of findCommentsByUsername method, of class PublicationService.
     */
    @Test
    public void testFindCommentsByUsername() {
        log.debug("testFindCommentsByUsername");
        publicationService.setCommentService(commentService);
        List>> result = publicationService.findCommentsByUsername("Fred Brooks");

        assertThat(result).isNotEmpty();
        assertThat(result, hasSize(greaterThanOrEqualTo(2)));

        List data = result
                .stream()
                .map(Map.Entry::getValue)
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());

        assertThat(data).containsAnyOf("Fred Brooks");

    }

Reference:

Spring Framework reference

Similar Posts ..

Subscribe to our monthly newsletter. No spam, we promise !

Guest