从零手写 Spring 风格 IoC 容器:支持自动扫描与依赖注入

devtools/2025/3/23 0:14:24/

手写一个 Spring 风格的 IoC 容器(支持自动扫描和依赖注入)

一、前言

控制反转(IoC, Inversion of Control) 是 Spring 框架的核心机制之一,它将对象的创建和依赖管理交给 IoC 容器,避免了对象之间的强耦合。本文将从零实现一个支持自动扫描和依赖注入的 IoC 容器,以深入理解 IoC 的工作原理。


二、IoC 核心机制

一个完整的 IoC 容器通常包含以下功能:

  1. Bean 定义与管理:存储 Bean 的元信息(类、作用域、依赖等)。
  2. 依赖注入(DI, Dependency Injection):通过构造函数或 @Autowired 自动注入依赖。
  3. 自动扫描组件:基于 @Component 注解,自动发现并注册 Bean。
  4. 生命周期管理:支持初始化、销毁等回调。

三、手写 IoC 容器

1. 定义 BeanDefinition

BeanDefinition 主要用于存储 Bean 的元信息,如类对象、是否为单例等。

java">public class BeanDefinition {private Class<?> beanClass;private boolean singleton = true;public BeanDefinition(Class<?> beanClass) {this.beanClass = beanClass;}public Class<?> getBeanClass() {return beanClass;}public boolean isSingleton() {return singleton;}public void setSingleton(boolean singleton) {this.singleton = singleton;}
}

2. 自定义 @Component@Autowired 注解

@Component 注解

用于标注类,使 IoC 容器能自动扫描该类并注册。

java">import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default ""; // Bean 名称,默认使用类名(首字母小写)
}
@Autowired 注解

用于标注成员变量,IoC 容器会自动注入该类型的 Bean。

java">import java.lang.annotation.*;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

3. 设计 ApplicationContext 容器

IoC 容器负责:

  • 通过类路径扫描找到 @Component 标注的类并自动注册。
  • 支持**@Autowired 依赖注入**,自动注入所需的 Bean。
java">import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;public class ApplicationContext {private Map<String, Object> singletonBeans = new HashMap<>();public ApplicationContext(String basePackage) {scanComponents(basePackage);injectDependencies();}// 组件扫描private void scanComponents(String basePackage) {String path = basePackage.replace(".", "/");URL url = Thread.currentThread().getContextClassLoader().getResource(path);if (url == null) {throw new RuntimeException("No resources found for package: " + basePackage);}File dir = new File(url.getFile());for (File file : dir.listFiles()) {if (file.getName().endsWith(".class")) {String className = basePackage + "." + file.getName().replace(".class", "");try {Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(Component.class)) {registerBean(clazz);}} catch (ClassNotFoundException e) {throw new RuntimeException("Failed to load class: " + className, e);}}}}// 注册 Beanprivate void registerBean(Class<?> clazz) {Component component = clazz.getAnnotation(Component.class);String beanName = component.value().isEmpty() ? lowerFirst(clazz.getSimpleName()) : component.value();try {Object instance = clazz.getDeclaredConstructor().newInstance();singletonBeans.put(beanName, instance);} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {throw new RuntimeException("Failed to create bean: " + clazz.getName(), e);}}// 依赖注入private void injectDependencies() {for (Object bean : singletonBeans.values()) {Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {field.setAccessible(true);try {Object dependency = getBean(field.getType());field.set(bean, dependency);} catch (IllegalAccessException e) {throw new RuntimeException("Failed to inject dependency: " + field.getName(), e);}}}}}// 获取 Beanpublic Object getBean(String name) {return singletonBeans.get(name);}// 根据类型获取 Beanpublic Object getBean(Class<?> clazz) {return singletonBeans.values().stream().filter(clazz::isInstance).findFirst().orElseThrow(() -> new RuntimeException("No bean found for type: " + clazz.getName()));}// 首字母小写private String lowerFirst(String str) {return Character.toLowerCase(str.charAt(0)) + str.substring(1);}
}

4. 业务类示例

(1) UserRepository 组件
java">@Component
public class UserRepository {public void save() {System.out.println("User saved!");}
}
(2) UserService 组件
java">@Component
public class UserService {@Autowiredprivate UserRepository userRepository;public void registerUser() {userRepository.save();}
}

5. 测试 IoC 容器

java">public class IoCTest {public static void main(String[] args) {ApplicationContext context = new ApplicationContext("com.example");UserService userService = (UserService) context.getBean(UserService.class);userService.registerUser(); // 输出:User saved!}
}

四、总结

1. IoC 容器的实现核心

自动扫描:使用 @Component 进行类扫描,自动注册 Bean。
依赖注入:支持 @Autowired 注入,无需手动管理对象。
单例模式:默认所有 Bean 都是单例模式。

2. 进一步优化方向

🔹 支持 @Scope("prototype") —— 让 Bean 可选单例或多例。
🔹 支持 @PostConstruct 生命周期回调 —— 允许 Bean 初始化后执行自定义逻辑。
🔹 支持 @Qualifier —— 解决多个相同类型 Bean 的注入问题。


五、结语

通过本次手写 IoC 容器,我们深入理解了 Spring IoC 的核心机制,并实现了:

  • Bean 注册
  • 单例管理
  • 自动扫描
  • 依赖注入

你可以基于本示例进一步扩展,如支持 XML 配置、Bean 生命周期管理等,打造更完整的 IoC 容器。🚀


http://www.ppmy.cn/devtools/168561.html

相关文章

Python中的字典:深度解析与应用实践

一、字典的本质与特性 Python字典&#xff08;Dictionary&#xff09;是以**键值对&#xff08;Key-Value Pair&#xff09;**形式存储数据的无序集合&#xff0c;使用大括号{}定义。其核心特性包括&#xff1a; 快速查找&#xff1a;基于哈希表实现&#xff0c;通过键&#…

深度探索DeepSeek部署的安全底线

摘要 在本地部署DeepSeek时&#xff0c;必须严格遵守安全底线。攻击者可能通过服务接口对DeepSeek模型数据进行篡改&#xff0c;包括删除模型或修改模型训练数据。此外&#xff0c;攻击者还可能注入恶意代码或删除关键组件&#xff0c;从而导致服务崩溃。因此&#xff0c;在部署…

前端调试实战指南:从入门到高阶的完整解决方案

引言:调试的本质与价值 调试是程序员将理想代码映射到现实运行环境的关键过程。据统计,开发者平均将30%的工作时间用于调试。本指南将系统梳理现代前端调试技术体系,帮助开发者构建高效的调试工作流。 一、基础调试工具箱 1.1 浏览器开发者工具核心功能 元素调试(Elemen…

【深度学习】走向VQ-VAE模型

有监督任务和无监督任务 有监督任务&#xff08;Supervised Tasks&#xff09;的数据包含输入特征和对应的标签&#xff08;目标值&#xff09;&#xff0c;模型学习从输入到输出的映射关系&#xff0c;典型任务有分类、回归、序列标注等。无监督任务&#xff08;Unsupervised…

《基于Spring Boot+Vue的智慧养老系统的设计与实现》开题报告

个人主页:@大数据蟒行探索者 一、研究背景及国内外研究现状 1.研究背景 根据1982年老龄问题世界大会联合国制定的标准,如果一个国家中超过65岁的老人占全国总人口的7%以上,或者超过60岁的老人占全国总人口的10%以上,那么这个国家将被定义为“老龄化社会”[1]。 随着国…

Git使用和原理(3)

1.远程操作 1.1分布式版本控制系统 我们⽬前所说的所有内容&#xff08;⼯作区&#xff0c;暂存区&#xff0c;版本库等等&#xff09;&#xff0c;都是在本地&#xff01;也就是在你的笔记本或者 计算机上。⽽我们的 Git 其实是分布式版本控制系统&#xff01;什么意思呢&a…

C#语法基础总结

输入和输出 输入 Console.Read(); 从屏幕读取一个字符&#xff0c;并返回该字符所对应的整型数字 Console.ReadLine(); 从屏幕读取一串字符&#xff0c;并返回该字符串 输出 Console.WriteLine(); 输出内容&#xff0c;并换行 Console.Write(); 输出内容&#xff0c;不换行…

在 Vue.js 中使用 Suspense 和异步组件优化应用性能

在现代前端开发中&#xff0c;应用的性能优化是一个非常重要的课题。随着应用规模的增大&#xff0c;初始加载时间可能会变长&#xff0c;用户体验也会受到影响。Vue.js 提供了强大的工具来优化性能&#xff0c;其中 异步组件 和 Suspense 是两个非常实用的特性。本文将详细介绍…