整体设计思路
- 定义依赖关系:使用注解或配置文件来标记需要注入的依赖。例如,可以定义一个
@Inject
注解,用于标记需要依赖注入的字段。
- 扫描组件:通过包扫描机制,利用反射获取所有需要进行依赖注入的类。遍历指定包下的所有类文件,加载并分析类的注解信息。
- 创建对象实例:使用反射创建类的实例。对于每个需要注入的类,通过
Class.forName()
获取类对象,然后使用newInstance()
方法创建实例(Java 9 之后推荐使用Constructor.newInstance()
)。
- 处理依赖注入:递归处理依赖关系,当发现某个类的字段被
@Inject
注解标记时,先创建该依赖类的实例,然后通过反射设置该字段的值。
- 处理循环依赖:
- 使用三级缓存:
- 一级缓存(
singletonObjects
):用于存储完全初始化好的单例对象,即已经完成依赖注入的对象。
- 二级缓存(
earlySingletonObjects
):用于存储早期暴露的单例对象,即已经创建但还未完成依赖注入的对象。
- 三级缓存(
singletonFactories
):用于存储单例对象工厂,当一级和二级缓存都没有找到对象时,尝试从这里获取对象。
- 在创建对象时,首先将对象工厂放入三级缓存,然后从三级缓存获取对象放入二级缓存,最后完成依赖注入后放入一级缓存。在注入依赖时,如果发现循环依赖,优先从二级缓存获取对象,避免死循环。
- 优化反射带来的性能损耗:
- 缓存反射信息:将反射获取的
Constructor
、Field
等信息缓存起来,避免每次创建对象或注入依赖时重复获取。可以使用ConcurrentHashMap
来存储这些信息,例如Map<Class<?>, Constructor<?>> constructorCache
。
- 使用字节码增强:在运行时通过字节码增强库(如ASM、Javassist)动态生成代码来代替反射操作。例如,使用Javassist生成获取和设置对象字段值的代码,性能比反射更好。
关键代码片段示例
- 定义
@Inject
注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {
}
- 依赖注入框架核心代码
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class DependencyInjector {
private static final Map<Class<?>, Object> singletonObjects = new HashMap<>();
private static final Map<Class<?>, Object> earlySingletonObjects = new HashMap<>();
private static final Map<Class<?>, Supplier<?>> singletonFactories = new HashMap<>();
public static <T> T getInstance(Class<T> clazz) {
if (singletonObjects.containsKey(clazz)) {
return (T) singletonObjects.get(clazz);
}
if (earlySingletonObjects.containsKey(clazz)) {
return (T) earlySingletonObjects.get(clazz);
}
Supplier<?> supplier = singletonFactories.get(clazz);
if (supplier != null) {
Object earlyInstance = supplier.get();
earlySingletonObjects.put(clazz, earlyInstance);
singletonFactories.remove(clazz);
return (T) earlyInstance;
}
try {
Constructor<T> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
T instance = constructor.newInstance();
singletonFactories.put(clazz, () -> instance);
earlySingletonObjects.put(clazz, instance);
injectDependencies(instance);
singletonObjects.put(clazz, instance);
earlySingletonObjects.remove(clazz);
return instance;
} catch (Exception e) {
throw new RuntimeException("Failed to create instance of " + clazz, e);
}
}
private static void injectDependencies(Object instance) throws Exception {
Class<?> clazz = instance.getClass();
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Class<?> dependencyType = field.getType();
Object dependency = getInstance(dependencyType);
field.set(instance, dependency);
}
}
}
}
- 使用示例
class ServiceB {
}
class ServiceA {
@Inject
private ServiceB serviceB;
public ServiceA() {
}
public void doSomething() {
System.out.println("ServiceA is using ServiceB: " + serviceB);
}
}
public class Main {
public static void main(String[] args) {
ServiceA serviceA = DependencyInjector.getInstance(ServiceA.class);
serviceA.doSomething();
}
}