Dependency injection trong java

Chia sẻ qua:

Overview

Dependency injection là gì?

Mình trích dẫn lại định nghĩa trên mạng thôi nhé.

Dependency Injection (DI): là một Design Pattern, một cách để hiện thực Inversion of Control Pattern. DI chính là khả năng liên kết giữa các thành phần lại với nhau, các module phụ thuộc (dependency) sẽ được inject vào module cấp cao. [Nguồn GPCODER] (https://gpcoder.com/4975-huong-dan-java-design-pattern-dependency-injection/)

Dùng DI trong java

Loại DI phổ biến thường sử dụng:

  1. Constructor DI

  2. Setter DI

Thay vì khởi tạo trực tiếp các biến phụ thuộc trong class thực hiện, ta truyền các phục thuộc vào thông qua Constructor hoăc Setter.

Lợi ích có thế thấy được đó là:

  1. Bạn có thể quyết định dùng implement nào khi xử lý thay vì quyết định khi viết class.

Ví dụ: Bạn cần lưu trữ và Database nhưng khi code chưa quyết định vào DB nào. Thì chỉ cần viết interface với hàm save và khi vào dự án có thể quyết định dùng impl nào, có thể là racle, MySQL ...

  1. Unit test dễ dàng hơn bằng cách mockup các dependency và injection thông qua constructor hoặc setter

Nhược điểm:

Phức tạp hơn khi tìm hiểu logic xử lý. Do việc dùng implement nào được truyền vào trong quá trình runtime.

Đây chỉ là một demo đơn giản. Ngoài các loại injection trên thì còn có thể inject thông qua reflection và abstract method …​

Trong các loại DI thì Constructor DI hiện tại khá phổ biến và được khuyên dùng.

Code tham khảo

https://github.com/dev4f/dependency-injection-demo

Ví dụ:

Một service lưu danh sách học sinh:

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class StudentService {
 7    
 8    private final OracleRepository oracleRepository = new OracleRepository();
 9    
10    public boolean addStudent(String studentName) { 
11        return oracleRepository.save(studentName);
12    }
13}

Một repository

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class OracleRepository {
 7    public boolean save(String studentName) {
 8        // Lưu vào DB (Mình chỉ tạm thời in ra đây thôi nhé)
 9        System.out.println("Save to oracle: " + studentName);
10        return true;
11    }
12}

Code này vẫn chạy tốt cho đến một ngày khách hàng của bạn quyết định dùng MySQL thay cho Oracle ta sẽ lại sửa lại service

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class StudentService {
 7    
 8    private final MysqlRepository mysqlRepository = new MysqlRepository();
 9    
10    public boolean addStudent(String studentName) { 
11        return mysqlRepository.save(studentName);
12    }
13}

Ok giờ dùng DI nhé

Chúng ta cần lưu học sinh vào một Database nào đó.

1package com.truongnq;
2
3/**
4 * @author truongnq
5 */
6public interface Repository {
7    boolean save(String studentName);
8}

Oracle repository

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class OracleRepository implements Repository {
 7    @Override
 8    public boolean save(String studentName) {
 9        // Lưu vào DB (Mình chỉ tạm thời in ra đây thôi nhé)
10        System.out.println("Save to oracle: " + studentName);
11        return true;
12    }
13}

Mysql repository

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class MysqlRepository implements Repository {
 7    @Override
 8    public boolean save(String studentName) {
 9        // Lưu vào DB (Mình chỉ tạm thời in ra đây thôi nhé)
10        System.out.println("Save to mysql: " + studentName);
11        return true;
12    }
13}

Service sử dụng Constructor DI

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class StudentService {
 7    
 8    private final Repository repository;
 9
10    public StudentService(Repository repository) {
11        this.repository = repository;
12    }
13
14    public boolean addStudent(String studentName) {
15        return repository.save(studentName);
16    }
17
18}

Khi đó bạn sẽ quyết định dùng DB nào khi khởi tạo service

 1package com.truongnq;
 2
 3/**
 4 * @author truongnq
 5 */
 6public class Main {
 7    public static void main(String[] args) {
 8        Repository repository = new OracleRepository();
 9        StudentService studentService = new StudentService(repository);
10        studentService.addStudent("truongnq.com");
11    }
12}

Một số annotation phổ biến khi sử dụng Dependency injection

@Autowired trong java spring

Trong spring bạn sẽ không cần phải khởi tạo tường minh OracleRepository hay MysqlRepository mà Spring IOC sẽ tự khởi tạo khi cần và bạn sử dụng

1@Component
2public class MyService {  
3    @Autowired
4    private Repository repository;
5}

@Autowired được sử dụng trên field và spring ioc sẽ tìm kiếm các implement của Repository và inject vào repository (Chú ý bạn phải đánh dấu class với annotation @Component thì spring mới thực hiện việc này)

Ngoài ra khi bạn sử dụng constructor injection

 1@Component
 2public class MyService {  
 3
 4    private Repository repository;
 5
 6
 7    public MyService(Repository repository) {
 8        this.repository = repository;
 9    }
10
11}

với spring khi thực hiện constructor injection bạn có thể không cần phải dùng @Autowired

@inject trong java với java cdi

Trong spring bạn sử dụng spring ioc cho việc quản lý và inject các object. Vậy nếu không sử dụng spring bạn phải làm thế nào.

Hãy đến với Java CDI, CDI (Contexts and Dependency Injection) là một chuẩn Java, cung cấp ngữ cảnh cho việc thực hiện Dependency Injection (DI).

Khi đó bạn có thể sử dụng DI như sau

1@Named("myService")
2public class MyService {  
3
4    @Inject
5    private Repository repository;
6
7}

@Named tương tự @Component với spring sẽ đánh dấu Class này sẽ được CDI quản lý và tạo ra khi cần thiết.

@Inject sẽ tìm implementation của Repository và inject vào biến repository