一个轻量级的依赖注入容器实现

在现代软件开发中,依赖注入(Dependency Injection, DI)是一种重要的设计模式,用于降低组件之间的耦合度,提高代码的可维护性和可测试性。虽然 Spring 框架是目前最流行的依赖注入框架之一,但在某些场景下,我们可能需要一个更轻量级的解决方案。本文将通过分析一个名为 MyContextHolder 的类,探讨如何实现一个基于静态容器的依赖注入框架,并支持通过注解进行依赖注入。


public class MyContextHolder {

    private static final Map, Object> MY_CONTAINER = new ConcurrentHashMap<>();

    private static volatile boolean isInit = false;

    public static void init() {
        if (isInit) {
            return;
        }
        synchronized (MyContextHolder.class) {
            if (isInit) {
                return;
            }
            initClass();
            isInit = true;
        }
    }

    private static void initClass() {
        try {
            register(A.class, B.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void autowire(Object obj) {
        Field[] declaredFields = obj.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(MyAutowired.class)) {
                Object dependency = MY_CONTAINER.get(field.getType());

                field.setAccessible(true);
                try {
                    field.set(obj, dependency);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private static void register(Class... classes) throws InstantiationException, IllegalAccessException {
        for (Class clazz : classes) {
            registerMyContainer(clazz, clazz.newInstance());
        }
        MY_CONTAINER.values().forEach(MyContextHolder::autowire);
    }

    private static void registerMyContainer(Class clazz, Object obj) {
        MY_CONTAINER.put(clazz, obj);
    }

    @SuppressWarnings("unchecked")
    public static  T getBean(Class clazz) {
        if (!isInit) {
            init();
        }
        T t = (T) MY_CONTAINER.get(clazz);
        PreconditionUtil.checkNotNullWithBiz(t, clazz.getSimpleName() + " not in MY_CONTAINER!");
        return t;
    }

}
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    boolean required() default true;
}

1. 概述

MyContextHolder 是一个轻量级的依赖注入容器,它通过静态方法和线程安全的 ConcurrentHashMap 来管理对象的生命周期和依赖关系。它支持通过注解 @MyAutowired 自动注入依赖,类似于 Spring 的 @Autowired 功能。这种实现方式不仅简单高效,还具有高度的灵活性。


2. 核心设计

MyContextHolder 的设计可以分为以下几个关键部分:

  1. 静态容器:使用 ConcurrentHashMap 作为核心容器,存储类与其实例的映射关系。

  2. 初始化机制:通过 init 方法初始化容器,确保线程安全。

  3. 依赖注册:通过 register 方法将类实例注册到容器中。

  4. 依赖注入:通过 autowire 方法,结合注解 @MyAutowired,实现依赖的自动注入。

  5. 依赖获取:通过 getBean 方法从容器中获取指定类的实例。


3. 初始化机制

MyContextHolder 的初始化机制通过 init 方法实现,确保容器只初始化一次:

java复制

public static void init() {
    if (isInit) {
        return;
    }
    synchronized (MyContextHolder.class) {
        if (isInit) {
            return;
        }
        initClass();
        isInit = true;
    }
}
  • 双重检查锁定:通过 volatilesynchronized 实现了双重检查锁定(Double-Check Locking),确保初始化过程的线程安全性。

  • 延迟初始化:只有在首次调用 initgetBean 方法时才会初始化容器,避免不必要的资源消耗。


4. 依赖注册与注入

MyContextHolder 通过 register 方法将类实例注册到容器中,并通过 autowire 方法实现依赖注入:

java复制

private static void register(Class... classes) throws InstantiationException, IllegalAccessException {
    for (Class clazz : classes) {
        registerMyContainer(clazz, clazz.newInstance());
    }
    MY_CONTAINER.values().forEach(MyContextHolder::autowire);
}
  • 自动实例化:对于传入的类,会自动调用其无参构造函数进行实例化。

  • 依赖注入:通过 autowire 方法,扫描对象的字段,查找带有 @MyAutowired 注解的字段,并从容器中获取对应的依赖实例注入。

依赖注入的实现如下:

java复制

private static void autowire(Object obj) {
    Field[] declaredFields = obj.getClass().getDeclaredFields();
    for (Field field : declaredFields) {
        if (field.isAnnotationPresent(MyAutowired.class)) {
            Object dependency = MY_CONTAINER.get(field.getType());

            field.setAccessible(true);
            try {
                field.set(obj, dependency);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  • 反射机制:通过反射获取对象的所有字段,并检查字段是否带有 @MyAutowired 注解。

  • 自动注入:根据字段的类型从容器中获取依赖实例,并通过反射设置字段的值。


5. 自定义注解 @MyAutowired

@MyAutowired 是一个自定义注解,用于标记需要自动注入的字段。它类似于 Spring 的 @Autowired 注解:

java复制

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
    boolean required() default true;
}
  • @Target:指定注解可以应用于字段。

  • @Retention:指定注解在运行时保留。

  • required:标记字段是否必须注入(默认为 true)。


6. 依赖获取

MyContextHolder 通过 getBean 方法从容器中获取指定类的实例:

java复制

@SuppressWarnings("unchecked")
public static  T getBean(Class clazz) {
    if (!isInit) {
        init();
    }
    T t = (T) MY_CONTAINER.get(clazz);
    PreconditionUtil.checkNotNullWithBiz(t, clazz.getSimpleName() + " not in MY_CONTAINER!");
    return t;
}
  • 自动初始化:如果容器尚未初始化,会自动调用 init 方法。

  • 类型安全:通过泛型和强制类型转换,确保返回的对象类型与请求的类型一致。

  • 异常处理:如果容器中不存在指定的类实例,会抛出异常,提醒开发者检查依赖关系。


7. 使用示例

以下是一个使用 MyContextHolder 的示例:

java复制

class A {
    @MyAutowired
    private B b;
}

class B {
    public String getName() {
        return "B class";
    }
}

public class Main {

    public static void main(String[] args) {
        MyContextHolder.init();

        A a = MyContextHolder.getBean(A.class);
        System.out.println(a.getB().getName()); 
    }

}

8. 优点与局限性

优点:

  1. 轻量级:不依赖外部框架,适合嵌入到已有项目中。

  2. 灵活性高:支持通过注解自动注入依赖,使用简单。

  3. 线程安全:通过 ConcurrentHashMap 和同步机制,确保线程安全。

  4. 易于扩展:可以通过扩展注解和容器功能,进一步增强其能力。

局限性:

  1. 功能有限:相比 Spring 等成熟框架,功能较为简单,缺乏生命周期管理、多实例支持等高级特性。

  2. 错误处理简单:异常处理较为简单,可能需要开发者在使用时进行额外的封装。

  3. 性能开销:反射操作会带来一定的性能开销,尤其是在大量字段注入时。


9. 总结

MyContextHolder 是一个轻量级的依赖注入容器,通过静态容器和注解机制,实现了简单而高效的依赖管理。它适用于对性能要求较高且不需要复杂依赖注入功能的场景。通过分析其设计和实现,我们可以更好地理解依赖注入的核心思想,并在实际开发中灵活运用。

在未来,我们可以通过引入更多高级特性(如生命周期管理、注解支持等)进一步扩展 MyContextHolder 的功能,使其成为一个更强大的依赖注入工具。

你可能感兴趣的:(java,开发语言)