在现代软件开发中,依赖注入(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;
}
MyContextHolder
是一个轻量级的依赖注入容器,它通过静态方法和线程安全的 ConcurrentHashMap
来管理对象的生命周期和依赖关系。它支持通过注解 @MyAutowired
自动注入依赖,类似于 Spring 的 @Autowired
功能。这种实现方式不仅简单高效,还具有高度的灵活性。
MyContextHolder
的设计可以分为以下几个关键部分:
静态容器:使用 ConcurrentHashMap
作为核心容器,存储类与其实例的映射关系。
初始化机制:通过 init
方法初始化容器,确保线程安全。
依赖注册:通过 register
方法将类实例注册到容器中。
依赖注入:通过 autowire
方法,结合注解 @MyAutowired
,实现依赖的自动注入。
依赖获取:通过 getBean
方法从容器中获取指定类的实例。
MyContextHolder
的初始化机制通过 init
方法实现,确保容器只初始化一次:
java复制
public static void init() {
if (isInit) {
return;
}
synchronized (MyContextHolder.class) {
if (isInit) {
return;
}
initClass();
isInit = true;
}
}
双重检查锁定:通过 volatile
和 synchronized
实现了双重检查锁定(Double-Check Locking),确保初始化过程的线程安全性。
延迟初始化:只有在首次调用 init
或 getBean
方法时才会初始化容器,避免不必要的资源消耗。
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
注解。
自动注入:根据字段的类型从容器中获取依赖实例,并通过反射设置字段的值。
@MyAutowired
@MyAutowired
是一个自定义注解,用于标记需要自动注入的字段。它类似于 Spring 的 @Autowired
注解:
java复制
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
boolean required() default true;
}
@Target
:指定注解可以应用于字段。
@Retention
:指定注解在运行时保留。
required
:标记字段是否必须注入(默认为 true
)。
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
方法。
类型安全:通过泛型和强制类型转换,确保返回的对象类型与请求的类型一致。
异常处理:如果容器中不存在指定的类实例,会抛出异常,提醒开发者检查依赖关系。
以下是一个使用 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());
}
}
轻量级:不依赖外部框架,适合嵌入到已有项目中。
灵活性高:支持通过注解自动注入依赖,使用简单。
线程安全:通过 ConcurrentHashMap
和同步机制,确保线程安全。
易于扩展:可以通过扩展注解和容器功能,进一步增强其能力。
功能有限:相比 Spring 等成熟框架,功能较为简单,缺乏生命周期管理、多实例支持等高级特性。
错误处理简单:异常处理较为简单,可能需要开发者在使用时进行额外的封装。
性能开销:反射操作会带来一定的性能开销,尤其是在大量字段注入时。
MyContextHolder
是一个轻量级的依赖注入容器,通过静态容器和注解机制,实现了简单而高效的依赖管理。它适用于对性能要求较高且不需要复杂依赖注入功能的场景。通过分析其设计和实现,我们可以更好地理解依赖注入的核心思想,并在实际开发中灵活运用。
在未来,我们可以通过引入更多高级特性(如生命周期管理、注解支持等)进一步扩展 MyContextHolder
的功能,使其成为一个更强大的依赖注入工具。