Avatar
2
TungDP Beginner
TungDP Beginner
Cách nhúng đối tượng tương tự như @Autowired của Spring boot?
Đặt vấn đề: Lúc đọc cuốn "Những Nguyên Tắc Sống Còn Trong Lập Trình" của anh Tạ Văn Dũng, em có khá ấn tượng về cách anh giải thích về ý tưởng Inversion-of-control. Sau đó, em cũng vọc vạch làm thử 1 cái tương tự, đại ý là em làm như này:
  • Bước 1: Dùng Reflection quét package của file Main để tìm ra toàn bộ các class được khai báo trong package, đưa vào 1 HashSet.
  • Bước 2: Tạo annotation @Instance, chạy vòng lặp: những class nào có đánh dấu @Instance thì khởi tạo instance cho nó, lưu vào 1 HashMap, Object>.
  • Bước 3: Tạo annotation @Inject, và tiếp tục lặp qua HashMap ở bước 2, nếu instance nào mà có các field được đánh dấu @Inject thì gắn object đã được tạo trong HashMap(bước 2) vào field đó.
Khó khăn: Nhưng em gặp vấn đề khó khi @Inject được đánh dấu vào field mà user lại sử dụng interface chứ không phải trực tiếp class. 2024-02-17_15-22.png Câu hỏi: Lúc này làm sao để có thể tìm được tất cả các class implements interface đó và chọn ra được class phù hợp để inject vào ạ?
  • Answer
Remain: 5
2 Answers
Avatar
toilahtc Beginner
toilahtc Beginner
Trong trường hợp này, bạn phải cung cấp các annotation để làm rõ thêm rằng bean nào sẽ được chỉ định cho interface này bằng cách cung cấp các annotation như @Qualifier hoặc @Primary (vui lòng tìm hiểu thêm trên internet nếu chưa rõ về 2 bean này) và như vậy thì spring container sẽ có thể hiểu và inject đúng bean cho bạn.
  • 1
  • Reply
Avatar
tvd12 Beginner
tvd12 Beginner
The Best Answer
Sorry em, anh reply chậm. Để có thể inject ở dạng interface thì em phải lưu map cả lớp cài đặt lẫn interface với cái bean em ạ, ví dụ:
public interface Ruler {}

và lớp cài đặt là:

public interface RulerImpl implements Ruler {}

Thì code DI của em có thể thế này:

@Retention(RetentionPolicy.RUNTIME)
@interface Instant {}

@Retention(RetentionPolicy.RUNTIME)
@interface Inject {}

class BeanManagement {
    public static void main(String[] args) throws Exception {
        EzyReflection reflections = new EzyReflectionProxy(
            "org.youngmonkeys.di"
        );
        Set<Class<?>> classes = reflections.getAnnotatedClasses(Instant.class);
        Map<String, Object> beanByName = new HashMap<>();
        Map<Class<?>, Object> beanByType = new HashMap<>();
        for (Class<?> clazz : classes) {
            Object bean = clazz
                .getDeclaredConstructor()
                .newInstance();
            beanByName.put(EzyClasses.getVariableName(clazz), bean);
            Class<?> parentClass = clazz;
            while (parentClass != null) {
                beanByType.put(parentClass, bean);
                parentClass = parentClass.getSuperclass();
            }
            for (Class<?> inf : clazz.getInterfaces()) {
                beanByType.put(inf, bean);
            }
            injectDependenciesToBean(beanByName, beanByType, bean);
        }
    }
    
    public static void injectDependenciesToBean(
        Map<String, Object> beanByName,
        Map<Class<?>, Object> beanByType,
        Object bean
    ) throws Exception {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Inject.class)) {
                // lấy phụ thuộc từ name trước, nếu không có thì lấy theo type
                Object dependency = beanByName.get(
                    EzyClasses.getVariableName(field.getType())
                );
                if (dependency == null) {
                    Class<?> parentClass = field.getType();
                    while (parentClass != null && dependency == null) {
                        dependency = beanByType.get(
                            parentClass
                        );
                        parentClass = parentClass.getSuperclass();
                    }
                    for (Class<?> inf : field.getType().getInterfaces()) {
                        dependency = beanByType.get(
                            inf
                        );
                        if (dependency != null) {
                            break;
                        }
                    }
                }
                field.setAccessible(true);
                field.set(bean, dependency);
            }
        }
    }
}

Theo cách này thì việc lấy ra bean nào sẽ phụ thuộc vào tên bean trước, sau đó đến type. Nếu giả sử có nhiều lớp cài đặt 1 interface thì có lẽ em nên đặt tên cho khớp để lấy ra được bean phù hợp em ạ.

  • 2
  • Reply
Dạ vâng em cảm ơn anh, anh có thể nói thêm giúp em cái đoạn này nó hoạt động như nào không ạ.

EzyReflection reflections = new EzyReflectionProxy(

"org.youngmonkeys.di"

)

 –  TungDP 1709242254000
Nó sẽ tìm trong classpath các file .jar hoặc .class và sau đó tìm đến các lớp nằm trong gói "org.youngmonkeys.di" em ạ.  –  tvd12 1709734402000