题目
设计基于反射和注解的轻量级依赖注入框架
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
自定义注解设计,反射动态处理,依赖注入原理,循环依赖检测,框架设计思想
快速回答
实现要点:
- 定义
@Inject注解标记需要注入的字段 - 创建
DIContainer类管理Bean生命周期 - 使用反射动态实例化对象并注入依赖
- 实现循环依赖检测机制
- 处理接口的多实现类场景
核心代码结构:
public class DIContainer {
private Map, Object> instances = new ConcurrentHashMap<>();
private Map, Class>> implementations = new ConcurrentHashMap<>();
public void register(Class> interfaceType, Class> implType) {...}
public T getInstance(Class type) {...}
}
## 解析
问题背景
在大型Java应用中,依赖注入(DI)是解耦组件的关键技术。要求设计一个轻量级DI框架,通过反射和注解实现以下功能:
- 使用
@Inject注解标记需要注入的依赖字段 - 支持接口与实现类的绑定注册
- 自动解决依赖关系(包括多层嵌套依赖)
- 检测并处理循环依赖
- 实现单例作用域管理
核心实现方案
1. 注解定义
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Inject {}2. 容器实现(关键代码)
public class DIContainer {
// 存储单例实例
private final Map, Object> singletonMap = new ConcurrentHashMap<>();
// 存储接口到实现类的映射
private final Map, Class>> interfaceToImpl = new ConcurrentHashMap<>();
// 正在创建的Bean标记(解决循环依赖)
private final Set> creatingBeans = Collections.newSetFromMap(new ConcurrentHashMap<>());
// 注册接口实现
public void register(Class> interfaceType, Class> implType) {
if (!interfaceType.isAssignableFrom(implType)) {
throw new IllegalArgumentException(implType + " must implement " + interfaceType);
}
interfaceToImpl.put(interfaceType, implType);
}
// 获取实例(核心方法)
public T getInstance(Class type) {
// 1. 获取实际实现类(处理接口情况)
Class> implType = interfaceToImpl.getOrDefault(type, type);
// 2. 单例检查
if (singletonMap.containsKey(implType)) {
return (T) singletonMap.get(implType);
}
// 3. 循环依赖检测
if (creatingBeans.contains(implType)) {
throw new CircularDependencyException("Detected circular dependency: " + implType);
}
creatingBeans.add(implType);
try {
// 4. 反射创建实例
T instance = createInstance(implType);
singletonMap.put(implType, instance);
return instance;
} finally {
creatingBeans.remove(implType);
}
}
private T createInstance(Class type) throws Exception {
// 获取无参构造器
Constructor constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
T instance = constructor.newInstance();
// 依赖注入处理
for (Field field : type.getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
Object dependency = getInstance(field.getType());
field.setAccessible(true);
field.set(instance, dependency);
}
}
return instance;
}
} 关键难点解析
循环依赖处理
使用creatingBeans集合标记正在创建的Bean:
- 在
getInstance()开始时检查当前类是否已在创建中 - 采用
ConcurrentHashMap保证线程安全 - 在finally块中移除标记确保异常场景下的正确性
接口绑定实现
// 注册接口实现
container.register(UserService.class, UserServiceImpl.class);
// 使用
UserService service = container.getInstance(UserService.class);最佳实践
- 作用域扩展:增加
@Scope("prototype")支持多例模式 - 构造器注入:扩展支持
@Inject注解在构造器上 - 性能优化:使用缓存避免重复反射操作
- 异常处理:自定义异常类型(如
NoImplementationException)
常见错误
| 错误类型 | 现象 | 解决方案 |
|---|---|---|
| 循环依赖未处理 | 栈溢出(StackOverflowError) | 使用creatingBeans标记检测 |
| 未注册接口实现 | ClassCastException | register()时校验isAssignableFrom() |
| 线程安全问题 | 并发创建多个单例 | 使用ConcurrentHashMap+双重检查锁 |
扩展知识
- 字节码增强方案:使用ByteBuddy或CGLIB生成代理类,避免反射性能损耗
- Spring框架对比:Spring使用三级缓存解决循环依赖(singletonFactories/earlySingletonObjects)
- JSR-330标准:javax.inject规范定义@Inject/@Singleton等标准注解
- 方法注入:扩展支持setter方法注入
完整使用示例
// 定义服务接口及实现
public interface UserService {
void saveUser(User user);
}
public class UserServiceImpl implements UserService {
@Inject
private UserRepository repository;
@Override
public void saveUser(User user) {
repository.save(user);
}
}
// 配置容器
DIContainer container = new DIContainer();
container.register(UserService.class, UserServiceImpl.class);
container.register(UserRepository.class, JdbcUserRepository.class);
// 获取服务实例
UserService service = container.getInstance(UserService.class);
service.saveUser(new User("test"));