Trong Spring Boot chúng ta có 3 kiểu inject các dependency là constructor, setter, field. Trong đó constructor injection được đa số cộng đồng khuyên sử dụng. Bài viết này chúng ta sẽ cùng nhau tìm hiểu lý do tại sao nhé.
Khởi tạo bean
Trước khi đi vào ví dụ constructor injection, chúng ta cần chuẩn bị bean đăng ký với ApplicationContext.
Chúng ta có Transmission class
package com.deft.constructorinjection; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public class Transmission { private String name; }
Và Engine dùng để tạo bean
package com.deft.constructorinjection; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public class Engine { private String name; private Integer number; }
Cuối cùng chúng ta sẽ tạo một config class có nhiệm vụ khởi tạo bean và đăng ký với IoC.
package com.deft.constructorinjection; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyConfig { @Bean public Engine engine() { return new Engine("v8", 5); } @Bean public Transmission transmission() { return new Transmission("sliding"); } }
Tiếp theo chúng ta sẽ định nghĩa MyService class sử dụng 2 dependency có kiểu dữ liệu là Engine và Tranmission.
package com.deft.constructorinjection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MyService { private final Engine engine; private final Transmission transmission; @Autowired public MyService(Engine engine, Transmission transmission) { this.engine = engine; this.transmission = transmission; } }
Trong ví dụ trên, mình đã chú thích @Autowired trong constructor của MyService nhận vào 2 tham số Engine và Tranmission.
Khi ứng dụng khởi chạy lên MyService sẽ được khởi tạo cùng với các dependency được khai báo trong constructor, Spring sẽ quét và tìm kiếm các bean tương ứng với Engine và Tranmission để tiêm vào MyService class để sử dụng.
Kể từ Spring 4.3, các class chỉ có một constructor duy nhất có thể bỏ qua @Autowired annotation. Điều này có vẽ tiện lợi, nhưng nó cũng sẽ gây hại nếu chúng ta sử dụng mà không hiểu điều này sẽ rất dễ gây lỗi khi chuyển qua lại giữa các phiên bản Spring. Giả sử bạn đang sử dụng Spring 4.3 thì không cần @Autowired còn nếu phiên bản thấp hơn thì phải sử dụng @Autowired.
Pros và Cons
Constructor injection có một vài ưu điểm hơn field injection. Lợi ích đầu tiên kể đến là khả năng kiểm thử. Gỉa sử chúng ta có một UserService được định nghĩa như sau:
public class UserService { @Autowired private UserRepository userRepository; }
Giả sử chúng ta có một unit-test dùng để kiểm thử UserService, trong qúa trình khởi tạo UserService, chúng ta sẽ không thể khởi tạo UserRepository. Cách duy nhất để đạt được điều này là thông qua API Reflection, API này phá vỡ hoàn toàn tính đóng gói.
Ngoài ra, với field injection chúng ta không thể khởi tạo UserService một cách đúng cách và có thể UserRepository sẽ không được khởi tạo. Điều này có thể dẫn đến NullPointerException.
Hơn nữa, việc sử dụng các constructor để khởi tạo các object phù hợp với khái niệm OOP.
Một điểm không tốt ở constructor injection là nó quá dài dòng, khi một class mà có nhiều phụ thuộc thì constructor sẽ chứa rất nhiều tham số khiến mã nguồn trở nên rất khó đọc.
Kết bài
Qua bài viết trên chúng ta đã có thể tìm hiểu sâu vào constructor injection và một số ưu, nhược điểm của nó. Bản thân mình và những dự án trước đây đều sử dụng field injection.
Nguồn tham khảo
https://www.baeldung.com/constructor-injection-in-spring