从零手写Spring IoC容器(一):传统对象管理的困境与基础容器的实现
从零手写Spring IoC容器(一):传统对象管理的困境与基础容器的实现
一、传统开发方式的问题
1.1 手动创建对象的痛点
在传统开发中,我们经常看到这样的代码:
public class OrderService {
// 直接在代码中创建依赖对象
private UserService userService = new UserService();
private PaymentService paymentService = new PaymentService();
private LogisticsService logisticsService = new LogisticsService();
private NotificationService notificationService = new EmailNotificationService();
public Order createOrder(String userId, List<Product> products) {
//业务方法
}
}
这种方式的三个主要问题:
- 紧耦合:对象创建代码散落在各处
- 难以管理:依赖关系复杂时容易出错
- 缺乏统一控制:无法实现统一的扩展点
1.2 IoC容器的解决方案
Spring框架通过BeanFactory
接口实现控制反转:
- 将对象的创建、装配过程交给容器
- 通过统一接口获取对象
- 支持多种配置方式(XML/注解)
二、基础容器实现
2.1 设计思路
2.2 核心接口设计
2.3定义核心接口
/**
* bean工厂顶级接口
* 用于提供基础的获取bean的功能
* @author 方
*/
public interface BeanFactory {
/**
* 根据bean名称获取bean实例
*
* @param name bean名称
* @return bean实例
* @throws BeansException 如果无法获取bean
*/
Object getBean(String name) throws BeansException;
/**
* 根据bean名称和类型获取bean实例
*
* @param name bean名称
* @param requiredType 需要的bean类型
* @return bean实例
* @throws BeansException 如果无法获取bean
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 根据类型获取bean实例
*
* @param requiredType 需要的bean类型
* @return bean实例
* @throws BeansException 如果无法获取bean
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 判断是否包含指定名称的bean
*
* @param name bean名称
* @return 如果包含返回true,否则返回false
*/
boolean containsBean(String name);
/**
* 判断指定名称的bean是否是单例
*
* @param name bean名称
* @return 如果是单例返回true,否则返回false
* @throws BeansException 如果无法判断bean的作用域
*/
boolean isSingleton(String name) throws BeansException;
/**
* 判断指定名称的bean是否是原型
*
* @param name bean名称
* @return 如果是原型返回true,否则返回false
* @throws BeansException 如果无法判断bean的作用域
*/
boolean isPrototype(String name) throws BeansException;
/**
* 获取指定名称bean的类型
*
* @param name bean名称
* @return bean的类型,如果不存在返回null
* @throws BeansException 如果无法获取bean的类型
*/
Class<?> getType(String name) throws BeansException;
}
2.4最简单的容器实现
public class SimpleBeanFactory implements BeanFactory {
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
/**
* 注册bean
* @param name
* @param bean
*/
public void registerBean(String name, Object bean) {
beanMap.put(name, bean);
}
/**
* 根据bean名称获取bean
*
* @param name bean名称
* @return bean对象
*/
@Override
public Object getBean(String name) {
if (containsBean(name)){
return beanMap.get(name);
}
throw new RuntimeException("no bean named " + name);
}
/**
* 根据bean名称和类型获取bean数据
*
* @param name bean名称
* @param requiredType bean的类型
* @return bean
*/
@Override
public <T> T getBean(String name, Class<T> requiredType) {
Object bean = getBean(name);
// 检查类型
if (requiredType.isInstance(bean)){
// 如果类型符合就转成这个类型返回出去
return requiredType.cast(bean);
}
return null;
}
/**
* 判断是否包含bean
*
* @param name bean名称
* @return boolean
*/
@Override
public boolean containsBean(String name) {
return beanMap.containsKey(name);
}
}
容器特点:
- 使用
ConcurrentHashMap
保证线程安全 - 提供简单的注册机制
- 按需抛出运行时异常
三、代码验证与使用示例
public class Test1 {
public static void main(String[] args) {
// 初始化容器
SimpleBeanFactory beanFactory = new SimpleBeanFactory();
// 注册Bean
UserService userService = new UserService();
beanFactory.registerBean("userService", userService);
// 获取Bean
UserService service = beanFactory.getBean("userService", UserService.class);
System.out.println("获取Bean成功: " + service);
}
}
四、不足之处与 Spring 的对比
虽然当前的 SimpleBeanFactory
已经实现了基本的 IoC 容器功能,能够手动注册和获取 Bean,但它仍然存在诸多局限性,与 Spring IoC 容器相比还有很大的提升空间。以下是几个关键问题:
4.1 属性依赖注入问题
目前 SimpleBeanFactory
只能通过 registerBean
方法手动注册对象,并且对象的依赖关系需要手动构造。例如:
UserRepository userRepository = new UserRepository();
UserService userService = new UserService(userRepository);
beanFactory.registerBean("userRepository", userRepository);
beanFactory.registerBean("userService", userService);
这种方式的问题:
- 需要手动实例化
UserRepository
并传递给UserService
,并未真正实现依赖注入(Dependency Injection)。 - 当
UserService
依赖多个组件时,构造过程会变得复杂。 - 如果
UserService
依赖于OrderService
,而OrderService
又依赖UserRepository
,维护起来会越来越困难。
改进方向:
在后续章节中,我们可以引入 构造器注入 和 Setter 注入,让 IoC 容器自动完成依赖注入,减少手动管理 Bean 依赖的复杂度。
4.2 Bean 的定义与注册
当前 SimpleBeanFactory
直接存储了 Object
类型的实例:
private Map<String, Object> beanMap = new ConcurrentHashMap<>();
但在实际应用中,我们通常需要提前 定义 Bean,而不是直接存储实例。Spring 采用 BeanDefinition 来描述 Bean,包括:
- Bean 的 class 类型
- Bean 的 作用域(singleton/prototype)
- Bean 的 依赖关系
- Bean 的 初始化方法与销毁方法
改进方向:
在下一步实现中,我们需要设计 BeanDefinition
类,并提供 基于 XML 或注解的 Bean 配置加载机制,从而支持更灵活的 Bean 注册方式。
4.3 Bean 的生命周期管理
目前 SimpleBeanFactory
只是简单地存储和返回对象,没有涉及 Bean 的完整生命周期管理。Spring 的 Bean 具有完整的生命周期,包括:
- 实例化(Instantiation):创建对象
- 属性填充(Dependency Injection):注入依赖
- 初始化(Initialization):调用
@PostConstruct
方法或init-method
- 使用(In Use):对象被应用层使用
- 销毁(Destruction):调用
@PreDestroy
方法或destroy-method
当前缺陷:
- 没有提供生命周期回调方法,例如 Bean 初始化和销毁时执行的逻辑。
- 缺少 BeanPostProcessor 机制,无法在 Bean 实例化前后进行增强,如 Spring AOP 代理的创建。
改进方向:
- 设计
BeanPostProcessor
机制,支持在 Bean 实例化前后进行自定义处理。 - 提供
init-method
和destroy-method
支持,管理 Bean 生命周期。
4.4 Bean 作用域管理
在 Spring IoC 容器中,Bean 作用域(Scope)是一个重要概念,主要包括:
- 单例模式(Singleton):容器只创建一个实例,所有请求返回同一对象(默认)。
- 原型模式(Prototype):每次获取 Bean 时,都会创建新的对象实例。
当前 SimpleBeanFactory
仅支持手动注册的对象,且默认认为所有 Bean 都是 单例,但 isSingleton
方法尚未实现:
五.引出第二章:实现 Bean 的定义与注册
当前的 SimpleBeanFactory
只能通过手动注册对象的方式管理 Bean,但在实际应用中,Bean 的管理需要更加灵活和可扩展。
在第二章中,我们将引入 BeanDefinition 概念,用于描述 Bean 的元信息,使容器可以根据定义来创建和管理 Bean。接下来的关键点包括:
- BeanDefinition 的设计
- Bean 的名称、类型、作用域(单例/原型)等元信息
- 构造参数和属性依赖的描述
- Bean 的注册机制
- 通过
BeanDefinition
统一管理 Bean 的定义 - 设计
BeanDefinitionRegistry
统一注册 Bean
- 通过
- 实例化策略(初步实现)
- 目前
SimpleBeanFactory
直接存储实例,后续需要基于BeanDefinition
动态创建对象 - 通过反射机制创建 Bean(暂不涉及 XML 或注解解析)
- 目前
关键点包括:
- BeanDefinition 的设计
- Bean 的名称、类型、作用域(单例/原型)等元信息
- 构造参数和属性依赖的描述
- Bean 的注册机制
- 通过
BeanDefinition
统一管理 Bean 的定义 - 设计
BeanDefinitionRegistry
统一注册 Bean
- 通过
- 实例化策略(初步实现)
- 目前
SimpleBeanFactory
直接存储实例,后续需要基于BeanDefinition
动态创建对象 - 通过反射机制创建 Bean(暂不涉及 XML 或注解解析)
- 目前
第二章将会完善 IoC 容器的 Bean 注册与定义机制,为后续的依赖注入、作用域管理和生命周期控制奠定基础。