初学者可用concurrenthashmap配合@mycomponent和@myautowired注解模拟di:先扫描包中带@mycomponent的类,反射创建实例并以小驼峰名存入map;再遍历已注册bean,对含@myautowired的字段,按类型或名称查map并注入,全程线程安全、日志清晰、逻辑分两轮。

初学者练手项目中,用 ConcurrentHashMap 配合自定义注解模拟依赖注入(DI),核心在于“轻量、可运行、逻辑清晰”——不追求功能完整,而重在理解容器管理、反射注入、线程安全这几个关键环节。下面直接说怎么做,不绕弯。
定义两个基础注解:@MyComponent 和 @MyAutowired
这是整个模拟的起点,就像贴标签一样告诉框架“这个类归我管”“这个字段要填东西”。
-
@MyComponent:标注在类上,表示该类需要被容器创建并托管;用
@Retention(RetentionPolicy.RUNTIME)确保运行时能读到 - @MyAutowired:标注在字段上,表示该字段需要自动注入一个已注册的 Bean;同样保留到运行时
用 ConcurrentHashMap 当 Bean 容器
不用 synchronized 或 Hashtable,就用 ConcurrentHashMap<string object></string> 存 Bean 实例,key 是 bean 名(比如类名首字母小写),value 是实例对象。
- 选它是因为:多线程下 put/get 安全,初学者不用自己加锁,也避免 HashMap 在并发场景下扩容出问题
- 初始化时扫描指定包路径下的所有 class,遇到带 @MyComponent 的类,就用反射创建实例,放进 map
- bean 名建议统一规则,例如
clazz.getSimpleName().substring(0,1).toLowerCase() + clazz.getSimpleName().substring(1)
扫描 + 注入:两步走,用反射完成
整个流程分两轮:第一轮创建所有单例 Bean 并注册;第二轮遍历这些 Bean,对其中带 @MyAutowired 的字段,从 map 中查对应类型或名称的 Bean,再用 Field.setAccessible(true) 注入进去。
- 扫描包路径可用
ClassLoader.getResource()获取 class 文件路径,再递归解析 .class 文件(简单起见,也可用Files.walk()扫 jar 或 classes 目录) - 字段注入时注意:如果字段类型是接口,就按接口类型找所有实现类中已注册的实例;若没找到,跳过或抛提示(别让初学者卡在空指针)
- 为防循环依赖,可加个正在创建中的集合(如
Set<class>></class>),发现重复进入就报错提示
写个启动类验证效果
新建几个测试类,比如 UserService 依赖 UserDao,都打上 @MyComponent;UserDao 字段在 UserService 里用 @MyAutowired 标记。然后手动调用你的容器初始化方法,最后 getBean("userService") 并调用方法看是否成功执行。
- 不需要 XML 或配置类,全部靠注解驱动
- 打印日志说明每一步在做什么(如“注册 UserDao → 实例已存入容器”“注入 UserService.userDao 字段”),方便调试
- 哪怕只支持字段注入、不支持构造器或 setter,也完全够练手理解 DI 本质











