如何为自研分析库构建原生依赖注入框架?
自研轻量依赖注入框架(按需实例化版)
核心思路
解决你之前一次性初始化所有类的问题,核心是懒加载+按需创建:不提前实例化所有类,而是在首次请求实例时才完成初始化,并缓存已创建的单例实例。通过反射解析依赖关系,递归创建所需的依赖对象。
关键组件实现
1. 定义核心注解
用自定义注解标记可被DI管理的类,以及需要注入的构造方法:
// 标记类可被DI容器管理,默认单例 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Injectable { boolean singleton() default true; } // 标记构造方法(用于构造注入) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.CONSTRUCTOR) public @interface Inject { }
2. 实现DI容器核心类
容器负责注册表管理、实例懒加载、依赖解析:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.ConcurrentHashMap; import java.util.Map; public class Injector { // 单例实例缓存:存储已创建的单例对象 private final Map<Class<?>, Object> singletonCache = new ConcurrentHashMap<>(); // 构造方法缓存:避免重复反射解析构造方法 private final Map<Class<?>, Constructor<?>> constructorCache = new ConcurrentHashMap<>(); // 获取实例的入口方法 @SuppressWarnings("unchecked") public <T> T getInstance(Class<T> clazz) { // 单例类先检查缓存 if (clazz.isAnnotationPresent(Injectable.class)) { Injectable injectable = clazz.getAnnotation(Injectable.class); if (injectable.singleton()) { Object instance = singletonCache.get(clazz); if (instance != null) { return (T) instance; } } } // 解析构造方法并创建实例 Constructor<?> constructor = getInjectConstructor(clazz); if (constructor == null) { throw new IllegalArgumentException("No valid constructor found for " + clazz.getName()); } // 递归创建构造方法的依赖参数 Object[] params = new Object[constructor.getParameterCount()]; for (int i = 0; i < params.length; i++) { Class<?> paramType = constructor.getParameterTypes()[i]; params[i] = getInstance(paramType); } // 实例化对象 try { T instance = (T) constructor.newInstance(params); // 单例类存入缓存 if (clazz.isAnnotationPresent(Injectable.class) && clazz.getAnnotation(Injectable.class).singleton()) { singletonCache.put(clazz, instance); } return instance; } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException("Failed to create instance for " + clazz.getName(), e); } } // 获取带@Inject注解的构造方法,无则尝试无参构造 private Constructor<?> getInjectConstructor(Class<?> clazz) { Constructor<?> cached = constructorCache.get(clazz); if (cached != null) { return cached; } // 优先找带@Inject的构造方法 for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { if (constructor.isAnnotationPresent(Inject.class)) { constructor.setAccessible(true); constructorCache.put(clazz, constructor); return constructor; } } // fallback到无参构造 try { Constructor<?> noArgConstructor = clazz.getDeclaredConstructor(); noArgConstructor.setAccessible(true); constructorCache.put(clazz, noArgConstructor); return noArgConstructor; } catch (NoSuchMethodException e) { return null; } } }
3. 框架使用示例
标记你的业务类:
// 单例分析追踪器 @Injectable public class AnalyticsTracker { private final EventLogger logger; // 构造注入依赖 @Inject public AnalyticsTracker(EventLogger logger) { this.logger = logger; } public void trackEvent(String event) { logger.log("Tracked event: " + event); } } // 单例事件日志器 @Injectable public class EventLogger { @Inject public EventLogger() { // 无参构造 } public void log(String message) { System.out.println(message); } } // 多实例会话类 @Injectable(singleton = false) public class UserSession { @Inject public UserSession() {} }
获取实例:
public class Main { public static void main(String[] args) { Injector injector = new Injector(); // 首次调用才初始化AnalyticsTracker和依赖的EventLogger AnalyticsTracker tracker = injector.getInstance(AnalyticsTracker.class); tracker.trackEvent("app_start"); // 单例验证:两次获取是同一个实例 AnalyticsTracker tracker2 = injector.getInstance(AnalyticsTracker.class); System.out.println(tracker == tracker2); // 输出true // 多实例验证:每次获取都是新对象 UserSession session1 = injector.getInstance(UserSession.class); UserSession session2 = injector.getInstance(UserSession.class); System.out.println(session1 == session2); // 输出false } }
可选优化点
- 循环依赖处理:添加
Set<Class<?>> creatingClasses记录正在创建的类,检测到循环依赖时抛出异常。 - 字段注入支持:实例创建后,遍历类的字段,注入带@Inject标记的私有字段。
- 预扫描优化:容器初始化时扫描指定包下的@Injectable类,提前解析构造方法元数据,减少运行时反射开销。
- 自定义作用域:扩展支持会话级、请求级等自定义作用域,不局限于单例/多实例。
内容的提问来源于stack exchange,提问作者SdkDroider177
相关产品推荐
相关产品推荐

