504. Java 反射 - 创建一个简单的依赖注入框架
1. 什么是依赖注入(DI)?
依赖注入(Dependency Injection,简称 DI )是一种设计模式,它的核心思想是: 👉 不要自己手动创建对象,而是让"工厂/框架"来为你准备好依赖对象并注入到业务类中。
这样做的好处:
- 解耦:业务类不再依赖具体实现,只依赖抽象。
- 易于测试:可以轻松替换依赖(比如替换成 Mock 实现)。
- 生命周期管理:框架可以统一管理对象的创建、销毁、单例复用等。
很多企业级框架(如 Spring、Jakarta EE、Guice)都是基于这种机制构建的。
2. 我们的目标
我们要写一个简化版的 DI 框架,支持两个功能:
- 单例模式 (Singleton):某些类只需要一个全局共享实例。
- 自动注入 (Injection):框架能自动扫描并填充需要的依赖字段。
3. 定义注解
我们先定义两个注解:
@Singleton:表示该类只能有一个实例。@Inject:表示该字段需要由框架来注入依赖。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Singleton {}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {}
4. 编写依赖工厂(DI 容器)
DependencyFactory 负责:
- 检查类上是否有
@Singleton注解,如果有则复用同一个实例。 - 使用反射扫描字段,找到
@Inject注解,并创建/注入依赖对象。
java
public class DependencyFactory {
private static final Map<Class<?>, Object> singletons = new HashMap<>();
public static <T> T getInstance(Class<T> clazz) {
try {
// ✅ 单例处理
if (clazz.isAnnotationPresent(Singleton.class)) {
if (singletons.containsKey(clazz)) {
return (T) singletons.get(clazz);
}
}
// ✅ 通过无参构造器创建对象
T instance = clazz.getConstructor().newInstance();
// ✅ 依赖注入
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Object dependency = getInstance(field.getType()); // 递归创建
field.set(instance, dependency);
}
}
// ✅ 保存单例
if (clazz.isAnnotationPresent(Singleton.class)) {
singletons.put(clazz, instance);
}
return instance;
} catch (Exception e) {
throw new RuntimeException("Failed to create instance for " + clazz, e);
}
}
}
5. 编写业务类示例
java
@Singleton
public class UserRepository {
public String getUser() {
return "User[Tom]";
}
}
public class UserService {
@Inject
private UserRepository repository;
public void processUser() {
System.out.println("Processing: " + repository.getUser());
}
}
6. 测试依赖注入
java
public class Main {
public static void main(String[] args) {
UserService service = DependencyFactory.getInstance(UserService.class);
service.processUser();
}
}
输出:
java
Processing: User[Tom]
这里 UserService 并没有自己 new UserRepository,而是通过 框架自动注入 得到的实例。
7. 总结
- 对比现实框架 :Spring 的
@Autowired、Jakarta EE 的@Inject就是类似机制。 - DI 的好处:降低耦合、集中管理依赖、便于扩展。
- 局限性:我们这里的框架比较简化,不支持作用域、懒加载、构造函数注入等高级特性。