手写一个 Spring 风格的 IoC 容器(支持自动扫描和依赖注入)
一、前言
控制反转(IoC, Inversion of Control) 是 Spring 框架的核心机制之一,它将对象的创建和依赖管理交给 IoC 容器,避免了对象之间的强耦合。本文将从零实现一个支持自动扫描和依赖注入的 IoC 容器,以深入理解 IoC 的工作原理。
二、IoC 核心机制
一个完整的 IoC 容器通常包含以下功能:
- Bean 定义与管理:存储 Bean 的元信息(类、作用域、依赖等)。
- 依赖注入(DI, Dependency Injection):通过构造函数或
@Autowired
自动注入依赖。 - 自动扫描组件:基于
@Component
注解,自动发现并注册 Bean。 - 生命周期管理:支持初始化、销毁等回调。
三、手写 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 容器。🚀