用map配合@component和@autowired注解模拟di:第一轮扫描包创建实例存入concurrenthashmap,第二轮按类型注入字段,支持自定义bean名与接口实现类查找。

用 Map 配合自定义注解模拟依赖注入,核心是“不靠框架、只靠反射+容器+规则”,让初学者看清 DI 是怎么一步步把对象串起来的。
定义两个关键注解
一个标类,一个标字段:
- @Component 加在类上,表示“这个类归容器管”;
- @Autowired 加在字段上,表示“这个字段要自动填值”。
两者都必须加 @Retention(RetentionPolicy.RUNTIME),否则运行时读不到。
用 ConcurrentHashMap 当容器
不用 HashMap,避免多线程下扩容出错;也不用手动加锁,ConcurrentHashMap 本身线程安全。
key 用 bean 名(比如 UserService → userService),value 存实例对象。
bean 名默认取类名首字母小写;也可支持 @Component("xxx") 自定义名称。
分两轮完成装配
第一轮:扫描指定包路径,找出所有带 @Component 的类,用 Class.forName + constructor.newInstance 创建实例,放进 Map。
第二轮:遍历已创建的每个实例,检查它的所有字段是否标了 @Autowired;
→ 按字段类型去 Map 中找匹配实例(by-type);
→ 找到就用 field.setAccessible(true) 强制设值;
→ 字段是接口?就找该接口的实现类中已注册的那个。
注意几个实操细节
- 扫描包路径可用 ClassLoader.getResource() 获取路径,再用 Files.walk 或 URLDecoder 处理 jar/class 目录;
- 注入前记得判断字段类型是否已在 Map 中存在,避免空指针;
- 循环依赖先不处理,发现正在创建中的类再次进入,直接抛异常提示;
- 容器建议封装成单例,init() 方法只调一次,getBean(Class
) 和 getBean(String) 都能取实例。
不复杂但容易忽略











