手撸 Spring 简易版 IOC
一、核心目标
基于 Spring 6.x 的核心思想,实现最简版 IOC 容器,包含:
- 注解扫描(@MyComponent、@MyConfiguration);
- BeanDefinition 封装;
- 单例 Bean 实例化;
- @MyAutowired 依赖注入;
- 兼容 Spring 6.x 的核心流程(简化版)。
二、完整实现代码
步骤 1:定义核心注解(模拟 Spring 6.x 注解)
import java.lang.annotation.*;
// 对应 Spring 的 @Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
String value() default "";
}
// 对应 Spring 的 @Autowired
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
}
// 对应 Spring 的 @Configuration + @ComponentScan
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConfiguration {
String scanPackage() default "";
}
步骤 2:定义 BeanDefinition(核心元信息类)
/** * 模拟 Spring 的 BeanDefinition:存储 Bean 的元信息 */
public class MyBeanDefinition {
// Bean 对应的类
private Class<?> beanClass;// Bean 作用域(默认单例)private String scope = "singleton";// 是否懒加载private boolean lazyInit = false;public MyBeanDefinition(Class<?> beanClass) {this.beanClass = beanClass;}// getter/setterpublic Class<?> getBeanClass() { return beanClass; }public void setBeanClass(Class<?> beanClass) { this.beanClass = beanClass; }public String getScope() { return scope; }public void setScope(String scope) { this.scope = scope; }public boolean isLazyInit() { return lazyInit; }public void setLazyInit(boolean lazyInit) { this.lazyInit = lazyInit; }}
步骤 3:实现核心 IOC 容器(MyApplicationContext)
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/** * 简易版 Spring 6.x IOC 容器 * 核心流程:扫描→注册 BeanDefinition→实例化 Bean→依赖注入 */
public class MyApplicationContext {
// 存储 BeanDefinition(对应 Spring 的 beanDefinitionMap)
private Map<String, MyBeanDefinition> beanDefinitionMap = new HashMap<>();// 单例池(对应 Spring 的 singletonObjects)private Map<String, Object> singletonObjects = new HashMap<>();// 配置类private Class<?> configClass;// 构造方法:初始化容器public MyApplicationContext(Class<?> configClass) {this.configClass = configClass;// 1. 扫描包(优化:支持中文路径+递归),注册 BeanDefinitionscanAndRegisterBeanDefinitions();// 2. 实例化所有非懒加载的单例 BeaninstantiateSingletons();}// 核心优化:扫描包(支持中文路径 + 递归扫描子包)private void scanAndRegisterBeanDefinitions() {// 获取扫描包路径MyConfiguration configAnnotation = configClass.getAnnotation(MyConfiguration.class);String scanPackage = configAnnotation.scanPackage();if (scanPackage.isEmpty()) {throw new RuntimeException("请配置 scanPackage 路径!");}// 转换包路径为文件路径(com.example.中文包 -> com/example/中文包)String packagePath = scanPackage.replace(".", "/");ClassLoader classLoader = Thread.currentThread().getContextClassLoader();URL resource = classLoader.getResource(packagePath);if (resource == null) {throw new RuntimeException("扫描包不存在:" + scanPackage);}try {// 核心优化1:解码中文路径(解决中文包名乱码问题)String decodedPath = URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8.name());File packageDir = new File(decodedPath);// 核心优化2:递归扫描目录(包含所有子包)recursiveScan(packageDir, scanPackage);} catch (UnsupportedEncodingException e) {throw new RuntimeException("中文路径解码失败:" + e.getMessage(), e);}}// 递归扫描目录的核心方法private void recursiveScan(File dir, String basePackage) {// 1. 校验目录是否存在if (!dir.exists() || !dir.isDirectory()) {return;}// 2. 遍历目录下的所有文件/子目录File[] files = dir.listFiles();if (files == null) {return;}for (File file : files) {if (file.isDirectory()) {// 若是子目录:递归扫描(更新基础包名,如 com.example -> com.example.子包)String subPackage = basePackage + "." + file.getName();recursiveScan(file, subPackage);} else if (file.getName().endsWith(".class")) {// 若是 .class 文件:处理并注册 BeanDefinitionprocessClassFile(file, basePackage);}}}// 处理 .class 文件,生成并注册 BeanDefinitionprivate void processClassFile(File classFile, String basePackage) {try {// 1. 拼接类全限定名(如 com.example.中文包.UserService)String className = getClassName(classFile, basePackage);// 2. 加载类Class<?> clazz = Class.forName(className);// 3. 判断是否有 @MyComponent 注解if (clazz.isAnnotationPresent(MyComponent.class)) {MyComponent component = clazz.getAnnotation(MyComponent.class);// 生成 Bean 名称(优先注解值,否则类名首字母小写)String beanName = component.value().isEmpty()? toLowerCaseFirstChar(clazz.getSimpleName()): component.value();// 创建并注册 BeanDefinitionMyBeanDefinition beanDefinition = new MyBeanDefinition(clazz);beanDefinitionMap.put(beanName, beanDefinition);}} catch (ClassNotFoundException e) {throw new RuntimeException("加载类失败:" + e.getMessage(), e);}}// 工具方法:从 .class 文件路径拼接类全限定名(兼容中文路径)private String getClassName(File classFile, String basePackage) {// 获取文件绝对路径(已解码为 UTF-8)String filePath = classFile.getAbsolutePath();// 截取包路径部分(如 D:/project/classes/com/example/中文包/UserService.class -> com/example/中文包/UserService)String packagePath = basePackage.replace(".", File.separator);int startIndex = filePath.indexOf(packagePath);int endIndex = filePath.lastIndexOf(".class");// 转换为类全限定名(com/example/中文包/UserService -> com.example.中文包.UserService)String className = filePath.substring(startIndex, endIndex).replace(File.separator, ".");return className;}// 实例化所有非懒加载的单例 Bean(原有逻辑不变)private void instantiateSingletons() {for (String beanName : beanDefinitionMap.keySet()) {MyBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 只处理单例 + 非懒加载的 Beanif ("singleton".equals(beanDefinition.getScope()) && !beanDefinition.isLazyInit()) {// 创建 Bean 实例Object bean = createBean(beanName, beanDefinition);// 存入单例池singletonObjects.put(beanName, bean);}}}// 创建 Bean 实例 + 依赖注入(原有逻辑不变)private Object createBean(String beanName, MyBeanDefinition beanDefinition) {Class<?> beanClass = beanDefinition.getBeanClass();try {// 1. 通过无参构造创建实例Object beanInstance = beanClass.getDeclaredConstructor().newInstance();// 2. 处理 @MyAutowired 依赖注入populateBean(beanInstance);return beanInstance;} catch (Exception e) {throw new RuntimeException("创建 Bean 失败:" + beanName, e);}}// 依赖注入(原有逻辑不变)private void populateBean(Object beanInstance) {Class<?> clazz = beanInstance.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(MyAutowired.class)) {field.setAccessible(true);Class<?> fieldType = field.getType();// 根据类型从单例池找依赖 BeanObject dependencyBean = getBeanByType(fieldType);if (dependencyBean == null) {throw new RuntimeException("未找到依赖 Bean:" + fieldType.getName());}try {// 注入依赖field.set(beanInstance, dependencyBean);} catch (IllegalAccessException e) {throw new RuntimeException("注入依赖失败:" + field.getName(), e);}}}}// 对外提供获取 Bean 的方法(按类型)public <T> T getBean(Class<T> clazz) {for (Map.Entry<String, Object> entry : singletonObjects.entrySet()) {if (clazz.isInstance(entry.getValue())) {return (T) entry.getValue();}}throw new RuntimeException("未找到 Bean:" + clazz.getName());}// 对外提供获取 Bean 的方法(按名称)public Object getBean(String beanName) {Object bean = singletonObjects.get(beanName);if (bean == null) {throw new RuntimeException("未找到 Bean:" + beanName);}return bean;}// 工具方法:根据类型查找 Beanprivate Object getBeanByType(Class<?> type) {for (Object bean : singletonObjects.values()) {if (type.isInstance(bean)) {return bean;}}return null;}// 工具方法:首字母小写private String toLowerCaseFirstChar(String str) {if (str == null || str.isEmpty()) return str;char[] chars = str.toCharArray();chars[0] = Character.toLowerCase(chars[0]);return new String(chars);}}
步骤 4:测试代码
1. 编写业务类
// Dao 层
@MyComponent
public class UserDao {
public void queryUser() {
System.out.println("Spring 6.x 简易 IOC:查询用户信息");
}
}
// Service 层(依赖 UserDao)
@MyComponent
public class UserService {
@MyAutowired
private UserDao userDao;
public void getUser() {
userDao.queryUser();
System.out.println("Spring 6.x 简易 IOC:UserService 处理请求");
}
}
2. 编写配置类
@MyConfiguration(scanPackage = "com.example.spring6.ioc.demo")
public class AppConfig {
}
3. 编写测试类
public class MyIocTest {
public static void main(String[] args) {
// 1. 创建简易 IOC 容器
MyApplicationContext context = new MyApplicationContext(AppConfig.class);
// 2. 获取 Bean
UserService userService = context.getBean(UserService.class);
// 3. 调用方法
userService.getUser();
}
}
运行结果
Spring 6.x 简易 IOC:查询用户信息
Spring 6.x 简易 IOC:UserService 处理请求
三、简易版 vs Spring 6.x 原生源码对比
| 简易版 IOC 组件 | Spring 6.x 原生组件 | 作用 |
|---|---|---|
| MyBeanDefinition | BeanDefinition | 存储 Bean 元信息 |
| beanDefinitionMap | DefaultListableBeanFactory#beanDefinitionMap | 存储 BeanDefinition |
| singletonObjects | DefaultSingletonBeanRegistry#singletonObjects | 单例池 |
| MyApplicationContext | AnnotationConfigApplicationContext | 核心容器 |
| populateBean() | AbstractAutowireCapableBeanFactory#populateBean | 依赖注入 |
| createBean() | AbstractAutowireCapableBeanFactory#createBean | 创建 Bean 实例 |
关注我,每天5分钟,带你从 Java 小白变身编程高手!
点赞 + 关注+私信"IOC源码"获取手撸源码,让更多小伙伴一起进步!