系列文章第1篇 | 共3篇
难度:⭐⭐ | 适合人群:想深入理解Spring IoC的开发者
💥 开场:一次代码review的"灵魂拷问"
时间: 周三下午
地点: 会议室
事件: 代码评审
我: "这是我写的用户服务代码..."(信心满满)
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 直接new
public User getUser(Long id) {
return userDao.findById(id);
}
}
架构师老李: "等等,为什么要在这里new一个UserDao?"
我: "不new怎么用?" 🤔
老李: "如果以后要换一个实现类呢?比如从MySQL换到MongoDB?"
我: "那就改代码呗..." 😅
老李: "一个类还好,如果100个类都这样new,要改100次?"
我: "这..." 😰(语塞)
老李: "这就是为什么需要IoC(控制反转)和DI(依赖注入)。你不应该自己new对象,应该让Spring容器帮你管理。"
我: "IoC?DI?听说过但不太懂..." 😓
老李: "回去好好研究一下Spring的IoC容器,下次评审我要看到改进。"
回到工位,我陷入了沉思...
我: "什么是IoC?为什么不让我new对象?Spring到底做了什么?" 🤔
经过一番研究后: "原来IoC这么重要!而且自己也能实现!" 💡
🤔 第一问:什么是IoC和DI?
Q1:先看一个生活中的例子
场景:你要喝咖啡
方式A:自己做(传统方式)
你需要:
1. 买咖啡豆
2. 买咖啡机
3. 买磨豆机
4. 买水
5. 学习如何制作
6. 自己做咖啡
累不累? 累死了!😫
方式B:去咖啡店(IoC方式)
你只需要:
1. 告诉店员:"我要一杯拿铁"
2. 等着接咖啡
爽不爽? 爽!😎
类比到代码:
| 现实世界 | 代码世界 |
|---|---|
| 你 | UserService |
| 咖啡 | UserDao对象 |
| 自己做 | 自己new |
| 咖啡店 | Spring IoC容器 |
| 店员给你 | 依赖注入 |
IoC的核心思想: "别自己做,我(Spring)给你准备好!"
Q2:什么是IoC(控制反转)?
官方定义:
Inversion of Control(控制反转)是一种设计原则,将对象的创建和管理权从程序代码转移到外部容器。
人话版本:
传统方式(你控制):
public class UserService {
// 我自己创建依赖对象
private UserDao userDao = new UserDaoImpl();
// 我控制使用哪个实现类
}
IoC方式(容器控制):
public class UserService {
// 我只声明需要什么
private UserDao userDao;
// 具体用哪个实现,由Spring容器决定
// 容器会"注入"给我
}
控制反转了什么?
| 维度 | 传统方式 | IoC方式 |
|---|---|---|
| 谁创建对象? | 你自己new | 容器创建 |
| 谁管理对象? | 你自己管理 | 容器管理 |
| 谁决定依赖? | 代码中写死 | 配置文件/注解 |
| 控制权 | 在你手里 | 反转给容器 |
总结: 控制权从"你"反转到"容器",这就是控制反转!
Q3:什么是DI(依赖注入)?
官方定义:
Dependency Injection(依赖注入)是实现IoC的一种方式,通过外部注入依赖对象。
人话版本:
传统方式(主动获取):
public class UserService {
private UserDao userDao;
public UserService() {
// 我主动去"拿"依赖
this.userDao = new UserDaoImpl();
}
}
DI方式(被动接收):
public class UserService {
private UserDao userDao;
// 构造器注入:容器"给"我依赖
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
依赖注入的三种方式:
方式1:构造器注入
@Service
public class UserService {
private final UserDao userDao;
@Autowired // 容器通过构造器注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}
方式2:Setter注入
@Service
public class UserService {
private UserDao userDao;
@Autowired // 容器通过setter注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
方式3:字段注入
@Service
public class UserService {
@Autowired // 容器直接注入字段(反射)
private UserDao userDao;
}
Q4:IoC和DI是什么关系?
简单理解:
IoC(控制反转)
↓
是一种思想/原则
↓
DI(依赖注入)
↓
是实现IoC的一种方式
类比:
- IoC = "我要喝咖啡"(目标)
- DI = "店员给我咖啡"(实现方式)
记忆口诀:
IoC是思想,DI是手段;
控制要反转,依赖靠注入。
🎨 第二问:为什么需要IoC?
痛点1:代码耦合度高
没有IoC的代码:
public class UserService {
private UserDao userDao = new UserDaoMySQLImpl(); // 写死了
public User getUser(Long id) {
return userDao.findById(id);
}
}
问题:
- ❌ 如果要换成MongoDB实现,必须改代码
- ❌ 如果要换成Redis实现,又要改代码
- ❌ 100个地方用了,要改100次
有IoC的代码:
@Service
public class UserService {
@Autowired
private UserDao userDao; // 不指定具体实现
public User getUser(Long id) {
return userDao.findById(id);
}
}
// 配置文件或@Configuration中决定用哪个实现
@Bean
public UserDao userDao() {
return new UserDaoMySQLImpl(); // 只需改这一处
// return new UserDaoMongoDBImpl(); // 换实现只改这里
}
优势: ✅ 解耦!改实现只需改配置,不改业务代码!
痛点2:对象创建复杂
没有IoC:
public class OrderService {
public Order createOrder() {
// 创建一个Order需要很多依赖
UserDao userDao = new UserDaoImpl();
ProductDao productDao = new ProductDaoImpl();
InventoryService inventoryService = new InventoryService(productDao);
PaymentService paymentService = new PaymentService();
NotificationService notificationService = new NotificationService();
// 天啊,这才能开始创建Order...
Order order = new Order();
// ...
return order;
}
}
问题:
- ❌ 创建一个对象要new一堆依赖
- ❌ 依赖关系复杂,容易遗漏
- ❌ 代码冗长,难以维护
有IoC:
@Service
public class OrderService {
@Autowired private UserDao userDao;
@Autowired private ProductDao productDao;
@Autowired private InventoryService inventoryService;
@Autowired private PaymentService paymentService;
@Autowired private NotificationService notificationService;
public Order createOrder() {
// 所有依赖都准备好了,直接用!
Order order = new Order();
// ...
return order;
}
}
优势: ✅ 简洁!容器帮你管理所有依赖!
痛点3:单例难以控制
没有IoC:
// 手写单例
public class ConfigService {
private static ConfigService instance;
private ConfigService() {}
public static ConfigService getInstance() {
if (instance == null) {
synchronized (ConfigService.class) {
if (instance == null) {
instance = new ConfigService();
}
}
}
return instance;
}
}
问题:
- ❌ 单例模式代码繁琐
- ❌ 多线程安全要自己处理
- ❌ 每个单例类都要写一遍
有IoC:
@Service
@Scope("singleton") // 默认就是单例
public class ConfigService {
// 啥都不用写,Spring自动管理单例
}
优势: ✅ 自动单例!不用写繁琐代码!
💻 第三问:手写100行代码实现简易IoC容器
目标
实现一个最简单的IoC容器,支持:
- ✅ Bean的注册
- ✅ Bean的创建
- ✅ 依赖注入(构造器注入)
- ✅ 单例管理
实现步骤
步骤1:定义Bean定义类
package com.example.myioc;
/**
* Bean定义类
* 存储Bean的元数据信息
*/
public class BeanDefinition {
private String beanName; // Bean名称
private Class<?> beanClass; // Bean类型
private boolean singleton; // 是否单例
public BeanDefinition(String beanName, Class<?> beanClass) {
this.beanName = beanName;
this.beanClass = beanClass;
this.singleton = true; // 默认单例
}
// Getter和Setter
public String getBeanName() {
return beanName;
}
public Class<?> getBeanClass() {
return beanClass;
}
public boolean isSingleton() {
return singleton;
}
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
}
步骤2:实现简易IoC容器
package com.example.myioc;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 简易IoC容器
* 核心功能:Bean注册、创建、依赖注入
*/
public class SimpleIoCContainer {
// 存储Bean定义(配方)
private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
// 存储单例Bean实例(成品)
private Map<String, Object> singletonBeans = new ConcurrentHashMap<>();
/**
* 注册Bean定义
*/
public void registerBeanDefinition(String beanName, Class<?> beanClass) {
BeanDefinition definition = new BeanDefinition(beanName, beanClass);
beanDefinitions.put(beanName, definition);
System.out.println("注册Bean: " + beanName + " -> " + beanClass.getSimpleName());
}
/**
* 获取Bean实例
*/
public Object getBean(String beanName) {
// 1. 检查是否已注册
if (!beanDefinitions.containsKey(beanName)) {
throw new RuntimeException("Bean不存在: " + beanName);
}
BeanDefinition definition = beanDefinitions.get(beanName);
// 2. 如果是单例,先从缓存中获取
if (definition.isSingleton() && singletonBeans.containsKey(beanName)) {
System.out.println("从缓存获取单例Bean: " + beanName);
return singletonBeans.get(beanName);
}
// 3. 创建Bean实例
Object bean = createBean(definition);
// 4. 如果是单例,放入缓存
if (definition.isSingleton()) {
singletonBeans.put(beanName, bean);
System.out.println("单例Bean已缓存: " + beanName);
}
return bean;
}
/**
* 创建Bean实例(核心方法)
*/
private Object createBean(BeanDefinition definition) {
Class<?> beanClass = definition.getBeanClass();
try {
// 1. 获取所有构造器
Constructor<?>[] constructors = beanClass.getConstructors();
if (constructors.length == 0) {
// 没有公共构造器,使用默认构造器
return beanClass.getDeclaredConstructor().newInstance();
}
// 2. 使用第一个构造器(简化处理)
Constructor<?> constructor = constructors[0];
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == 0) {
// 无参构造器
System.out.println("使用无参构造器创建: " + beanClass.getSimpleName());
return constructor.newInstance();
}
// 3. 有参构造器,需要注入依赖(依赖注入的核心!)
System.out.println("使用有参构造器创建: " + beanClass.getSimpleName());
Object[] args = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> paramType = parameterTypes[i];
// 根据类型查找对应的Bean
String dependencyBeanName = findBeanNameByType(paramType);
if (dependencyBeanName != null) {
// 递归获取依赖的Bean(依赖注入)
args[i] = getBean(dependencyBeanName);
System.out.println(" 注入依赖: " + paramType.getSimpleName());
} else {
throw new RuntimeException("找不到类型为 " + paramType + " 的Bean");
}
}
return constructor.newInstance(args);
} catch (Exception e) {
throw new RuntimeException("创建Bean失败: " + beanClass, e);
}
}
/**
* 根据类型查找Bean名称
*/
private String findBeanNameByType(Class<?> type) {
for (Map.Entry<String, BeanDefinition> entry : beanDefinitions.entrySet()) {
Class<?> beanClass = entry.getValue().getBeanClass();
// 检查是否是该类型或其子类
if (type.isAssignableFrom(beanClass)) {
return entry.getKey();
}
}
return null;
}
/**
* 获取所有Bean名称
*/
public void printAllBeans() {
System.out.println("\n===== 容器中的所有Bean =====");
beanDefinitions.keySet().forEach(name ->
System.out.println("- " + name)
);
System.out.println("============================\n");
}
}
步骤3:创建测试类
UserDao接口和实现:
package com.example.myioc.demo;
// DAO接口
public interface UserDao {
String findUserById(Long id);
}
// DAO实现
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl 被创建");
}
@Override
public String findUserById(Long id) {
return "User-" + id;
}
}
UserService:
package com.example.myioc.demo;
public class UserService {
private final UserDao userDao;
// 构造器注入
public UserService(UserDao userDao) {
System.out.println("UserService 被创建,依赖已注入");
this.userDao = userDao;
}
public String getUser(Long id) {
return userDao.findUserById(id);
}
}
OrderService(依赖UserService):
package com.example.myioc.demo;
public class OrderService {
private final UserService userService;
// 构造器注入
public OrderService(UserService userService) {
System.out.println("OrderService 被创建,依赖已注入");
this.userService = userService;
}
public String createOrder(Long userId) {
String user = userService.getUser(userId);
return "Order created for " + user;
}
}
步骤4:测试我们的IoC容器
package com.example.myioc;
import com.example.myioc.demo.*;
public class IoCTest {
public static void main(String[] args) {
System.out.println("========== 开始测试简易IoC容器 ==========\n");
// 1. 创建IoC容器
SimpleIoCContainer container = new SimpleIoCContainer();
// 2. 注册Bean定义
System.out.println("--- 步骤1:注册Bean ---");
container.registerBeanDefinition("userDao", UserDaoImpl.class);
container.registerBeanDefinition("userService", UserService.class);
container.registerBeanDefinition("orderService", OrderService.class);
// 3. 查看所有Bean
container.printAllBeans();
// 4. 获取Bean(测试依赖注入)
System.out.println("--- 步骤2:获取OrderService ---");
OrderService orderService = (OrderService) container.getBean("orderService");
// 5. 使用Bean
System.out.println("\n--- 步骤3:使用Bean ---");
String result = orderService.createOrder(123L);
System.out.println("结果: " + result);
// 6. 再次获取(测试单例)
System.out.println("\n--- 步骤4:再次获取OrderService(测试单例) ---");
OrderService orderService2 = (OrderService) container.getBean("orderService");
System.out.println("是否是同一个实例: " + (orderService == orderService2));
System.out.println("\n========== 测试完成 ==========");
}
}
运行结果
========== 开始测试简易IoC容器 ==========
--- 步骤1:注册Bean ---
注册Bean: userDao -> UserDaoImpl
注册Bean: userService -> UserService
注册Bean: orderService -> OrderService
===== 容器中的所有Bean =====
- userDao
- userService
- orderService
============================
--- 步骤2:获取OrderService ---
使用有参构造器创建: OrderService
使用有参构造器创建: UserService
注入依赖: UserDao
使用无参构造器创建: UserDaoImpl
UserDaoImpl 被创建
单例Bean已缓存: userDao
注入依赖: UserService
UserService 被创建,依赖已注入
单例Bean已缓存: userService
注入依赖: UserService
OrderService 被创建,依赖已注入
单例Bean已缓存: orderService
--- 步骤3:使用Bean ---
结果: Order created for User-123
--- 步骤4:再次获取OrderService(测试单例) ---
从缓存获取单例Bean: orderService
是否是同一个实例: true
========== 测试完成 ==========
代码解析
关键点1:Bean定义的存储
// 存储Bean的"配方"
private Map<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();
关键点2:单例缓存
// 存储单例Bean实例的"仓库"
private Map<String, Object> singletonBeans = new ConcurrentHashMap<>();
关键点3:依赖注入的实现
// 创建Bean时,递归获取依赖
for (int i = 0; i < parameterTypes.length; i++) {
String dependencyBeanName = findBeanNameByType(parameterTypes[i]);
args[i] = getBean(dependencyBeanName); // 递归!
}
流程图:
用户调用 getBean("orderService")
↓
检查单例缓存(没有)
↓
创建 OrderService
↓
发现需要 UserService 依赖
↓
递归调用 getBean("userService")
↓
创建 UserService
↓
发现需要 UserDao 依赖
↓
递归调用 getBean("userDao")
↓
创建 UserDaoImpl(无依赖)
↓
返回 UserDaoImpl,缓存
↓
返回 UserService,缓存
↓
返回 OrderService,缓存
我们实现了什么?
✅ IoC(控制反转)
- Bean的创建权交给了容器
- 不再是用户自己new
✅ DI(依赖注入)
- 通过构造器自动注入依赖
- 递归解决依赖链
✅ 单例管理
- 自动缓存单例Bean
- 第二次获取直接从缓存返回
✅ 只用了100行代码!
🔍 第四问:Spring的BeanFactory是什么?
从我们的简易版到Spring
我们的容器:
SimpleIoCContainer container = new SimpleIoCContainer();
container.registerBeanDefinition("userDao", UserDaoImpl.class);
Object bean = container.getBean("userDao");
Spring的容器:
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
// 或
BeanFactory factory = new DefaultListableBeanFactory();
Object bean = factory.getBean("userDao");
发现: 接口名都叫BeanFactory!思路是一样的!
BeanFactory接口
位置: org.springframework.beans.factory.BeanFactory
核心方法:
public interface BeanFactory {
/**
* 根据名称获取Bean
*/
Object getBean(String name) throws BeansException;
/**
* 根据名称和类型获取Bean
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 根据类型获取Bean
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 检查是否包含指定名称的Bean
*/
boolean containsBean(String name);
/**
* 检查指定Bean是否单例
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 检查指定Bean是否原型
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 获取Bean的类型
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
}
看到了吗? 和我们的简易版接口类似!
BeanFactory的核心实现类
1. DefaultListableBeanFactory
最常用的实现类,几乎所有功能都有:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 注册Bean
BeanDefinition definition = new RootBeanDefinition(UserService.class);
factory.registerBeanDefinition("userService", definition);
// 获取Bean
UserService service = factory.getBean("userService", UserService.class);
2. XmlBeanFactory(已废弃)
通过XML配置文件创建:
// beans.xml
<beans>
<bean id="userDao" class="com.example.UserDaoImpl"/>
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userDao"/>
</bean>
</beans>
// Java代码
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
UserService service = (UserService) factory.getBean("userService");
注意: Spring 3.1后已废弃,推荐用ApplicationContext
手写示例:使用BeanFactory
package com.example.spring;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class BeanFactoryDemo {
public static void main(String[] args) {
System.out.println("===== BeanFactory演示 =====\n");
// 1. 创建BeanFactory
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// 2. 注册Bean定义
System.out.println("--- 注册Bean定义 ---");
factory.registerBeanDefinition("userDao",
new RootBeanDefinition(UserDaoImpl.class));
factory.registerBeanDefinition("userService",
new RootBeanDefinition(UserService.class));
// 3. 获取Bean
System.out.println("\n--- 第一次获取Bean ---");
UserService service1 = factory.getBean("userService", UserService.class);
service1.getUser(1L);
// 4. 再次获取(测试单例)
System.out.println("\n--- 第二次获取Bean ---");
UserService service2 = factory.getBean("userService", UserService.class);
System.out.println("是否同一实例: " + (service1 == service2));
// 5. 检查Bean信息
System.out.println("\n--- Bean信息 ---");
System.out.println("包含userService: " + factory.containsBean("userService"));
System.out.println("是否单例: " + factory.isSingleton("userService"));
System.out.println("Bean类型: " + factory.getType("userService"));
System.out.println("\n===== 演示完成 =====");
}
}
输出:
===== BeanFactory演示 =====
--- 注册Bean定义 ---
--- 第一次获取Bean ---
UserDaoImpl 被创建
UserService 被创建,依赖已注入
User-1
--- 第二次获取Bean ---
是否同一实例: true
--- Bean信息 ---
包含userService: true
是否单例: true
Bean类型: class com.example.UserService
===== 演示完成 =====
BeanFactory的特点
| 特点 | 说明 |
|---|---|
| 懒加载 | 调用getBean()时才创建Bean |
| 轻量级 | 功能基础,占用内存少 |
| 手动配置 | 需要手动注册Bean定义 |
| 适用场景 | 资源受限环境、嵌入式系统 |
🎯 第五问:BeanFactory vs 我们的简易版
功能对比
| 功能 | 我们的简易版 | Spring BeanFactory |
|---|---|---|
| Bean注册 | ✅ 支持 | ✅ 支持 |
| Bean创建 | ✅ 支持 | ✅ 支持 |
| 依赖注入 | ✅ 构造器注入 | ✅ 构造器+Setter+字段 |
| 单例管理 | ✅ 支持 | ✅ 支持 |
| 原型模式 | ❌ 不支持 | ✅ 支持 |
| 生命周期回调 | ❌ 不支持 | ✅ 支持 |
| 循环依赖 | ❌ 不支持 | ✅ 支持(三级缓存) |
| AOP集成 | ❌ 不支持 | ✅ 支持 |
| 事件机制 | ❌ 不支持 | ❌ 不支持(需ApplicationContext) |
设计对比
我们的设计:
SimpleIoCContainer
├── beanDefinitions (Bean定义)
├── singletonBeans (单例缓存)
├── registerBeanDefinition() (注册)
├── getBean() (获取)
└── createBean() (创建)
Spring的设计:
BeanFactory (接口)
├── HierarchicalBeanFactory (层次化)
├── ListableBeanFactory (可列举)
└── DefaultListableBeanFactory (默认实现)
├── beanDefinitionMap (Bean定义)
├── singletonObjects (一级缓存)
├── earlySingletonObjects (二级缓存)
├── singletonFactories (三级缓存)
└── 各种BeanPostProcessor
差距: Spring的设计更复杂,但也更强大!
💡 知识点总结
本篇你学到了什么?
✅ IoC和DI的概念
- IoC = 控制反转(思想)
- DI = 依赖注入(实现方式)
- 控制权从代码转移到容器
✅ 为什么需要IoC?
- 解耦:降低代码耦合度
- 简化:容器管理对象创建
- 单例:自动管理单例模式
✅ 手写简易IoC容器
- 100行代码实现核心功能
- 理解了Bean注册、创建、注入流程
- 实现了单例缓存机制
✅ Spring的BeanFactory
- 最基础的IoC容器接口
- DefaultListableBeanFactory是核心实现
- 懒加载、轻量级特点
✅ 简易版vs Spring
- 思路相同,实现不同
- Spring功能更强大
- 理解原理更重要
🤔 思考题
问题1: 我们的简易IoC容器只支持构造器注入,如何改造支持Setter注入?
问题2: 如果两个Bean互相依赖(循环依赖),我们的容器会怎样?Spring是如何解决的?
问题3: BeanFactory和ApplicationContext有什么区别?为什么日常开发中都用ApplicationContext?
提示: 第二篇会详细解答!
📢 下期预告
《IoC容器深度解析(二):ApplicationContext深入剖析,7大区别你知道几个?》
下一篇我们将:
- 深入ApplicationContext接口
- 对比BeanFactory和ApplicationContext的7大区别
- 分析ApplicationContext的启动流程
- 实战:如何选择合适的容器
- 源码解析:ApplicationContext是如何扩展BeanFactory的
让你彻底搞懂Spring的两大核心容器! 🚀
💬 互动时间
你在实际开发中遇到过IoC相关的问题吗?
对我们手写的简易IoC容器有什么改进建议?
还有哪些关于IoC的疑问?
欢迎评论区讨论!💭
觉得有帮助?三连支持: 👍 点赞 | ⭐ 收藏 | 🔄 转发
看完这篇,IoC不再神秘! ✨
下一篇见! 👋