【Spring进阶】Spring IOC实现原理是什么?容器创建和对象创建的时机是什么?

👨‍💻程序员三明治个人主页
🔥 个人专栏 : 《设计模式精解》 《重学数据结构》

🤞先做到 再看见!


Bean的生命周期

1️⃣** 初始化容器**

java 复制代码
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  • ApplicationContextBeanFactory 的子接口;
  • 它在启动(refresh())时会完成 配置加载 + Bean 注册 + Bean 实例化
  • 这是一个预加载容器(启动即创建 Bean)。

⚠️ 与之相对的 BeanFactory 是延迟加载容器,只有在第一次调用 getBean() 时才会创建对象。

2️⃣** 解析配置并生成 BeanDefinition**

Spring 首先会读取配置文件(如 XML 或注解),解析 <bean> 标签中的定义,例如:

xml 复制代码
<bean id="myBean" class="com.example.MyBean" scope="singleton"/>

这些信息会被封装成 BeanDefinition 对象,描述 Bean 的元数据,包括:

属性 说明
id Bean 的唯一名称
class Bean 的全类名
scope 作用域(singleton / prototype)
constructor args / properties 依赖信息

BeanDefinition 不是 Bean 实例,而是告诉容器"如何创建 Bean"的蓝图。


3️⃣** 注册 BeanDefinition**

Spring 将所有的 BeanDefinition 注册到一个全局的注册表中:

java 复制代码
Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
beanDefinitionMap.put("myBean", BeanDefinition@1234);

📘 这一步只完成定义注册,还没有实例化对象。

当容器启动(ApplicationContext)或首次 getBean()BeanFactory)时,Spring 开始真正创建对象。

无论触发方式如何,最终都会进入相同的调用链:

java 复制代码
MyBean bean = ctx.getBean("myBean");

getBean()
 └── doGetBean()
      ├── getSingleton()             // 从缓存中查找是否已创建
      ├── createBean()               // 未创建则进入创建流程
      │    ├── createBeanInstance()  // 反射实例化对象
      │    ├── populateBean()        // 依赖注入
      │    └── initializeBean()      // 初始化回调
      └── addSingleton()             // 放入缓存池

1️⃣** createBeanInstance():反射创建对象**

java 复制代码
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
Object bean = clazz.getDeclaredConstructor().newInstance();
  • Spring 使用 反射 动态创建 Bean 实例;
  • 这一步只负责实例化,不注入依赖;
  • 对象创建后暂存在缓存中,等待依赖注入。

2️⃣** populateBean():执行依赖注入**

Spring 会根据 BeanDefinition 或注解信息完成依赖注入。

常见的三种方式:

注入方式 示例 实现方式
构造器注入 <constructor-arg> 反射调用构造器
Setter 注入 <property> 反射调用 setter 方法
字段注入 @Autowired / @Resource 反射直接设置字段值

示例:

java 复制代码
@Component
public class MyBean {
    @Autowired
    private MyService myService;
}

底层反射逻辑:

java 复制代码
Field field = clazz.getDeclaredField("myService");
field.setAccessible(true);
field.set(bean, context.getBean("myService"));

3️⃣** initializeBean():初始化与回调**

依赖注入完成后,Spring 会执行初始化回调,包含:

  1. 调用 BeanNameAwareApplicationContextAware 等接口;
  2. 执行 InitializingBean.afterPropertiesSet()
  3. 调用 init-method
  4. 触发 BeanPostProcessor(如 AOP 代理增强)。

4️⃣** addSingleton():放入单例缓存**

当 Bean 完全创建完毕后,Spring 会将其放入一级缓存:

java 复制代码
singletonObjects.put(beanName, bean);

这样之后再次 getBean("myBean"),会直接从缓存中取,不再重新创建。

java 复制代码
1️⃣ 读取配置文件或扫描注解
    ↓
2️⃣ 生成 BeanDefinition(Bean 蓝图)
    ↓
3️⃣ 注册到 beanDefinitionMap(定义注册)
    ↓
4️⃣ 调用 getBean() 或 容器启动触发创建
    ↓
5️⃣ createBean() → 反射创建对象
    ↓
6️⃣ populateBean() → 执行依赖注入
    ↓
7️⃣ initializeBean() → 初始化与增强
    ↓
8️⃣ addSingleton() → 缓存单例 Bean
    ↓
9️⃣ 返回 Bean 实例

说说BeanFactory和ApplicantContext?

可以这么形容,BeanFactory是Spring的"心脏",ApplicantContext是完整的"身躯"。

  • BeanFactory(Bean工厂)是Spring框架的基础设施,面向Spring本身。
  • ApplicantContext(应用上下文)建立在BeanFactoty基础上,面向使用Spring框架的开发者。

Spring为BeanFactory提供了很多种实现,最常用的是XmlBeanFactory,但在Spring3.2中已被废弃,建议使用XmlBeanDefinitionReader、DefaultListableBeanFactory。

BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String var1),这个方法从容器中返回特定名称的Bean。

BeanFactory的功能通过其它的接口得到了不断的扩展,比如AbstractAutowireCapableBeanFactory定义了将容器中的Bean按照某种规则(比如按名字匹配、按类型匹配等)进行自动装配的方法。

这里看一个 XMLBeanFactory(已过期) 获取bean 的例子:

ApplicationContext接口

ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。可以这么说,使用BeanFactory就是手动档,使用ApplicationContext就是自动档。

ApplicationContext 继承了HierachicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过其他的接口扩展了ApplicationContext的功能,包括:

  • Bean instantiation/wiring
  • Bean 的实例化/串联
  • 自动的 BeanPostProcessor 注册
  • 自动的 BeanFactoryPostProcessor 注册
  • 方便的 MessageSource 访问(i18n)
  • ApplicationEvent 的发布与 BeanFactory 懒加载的方式不同,它是预加载,所以,每一个 bean 都在 ApplicationContext 启动之后实例化

这是 ApplicationContext 的使用例子:

ApplicationContext 包含 BeanFactory 的所有特性,通常推荐使用前者。

如果我的内容对你有帮助,请辛苦动动您的手指为我点赞,评论,收藏。感谢大家!!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=h70g0sv71wz

相关推荐
Victor3561 小时前
Redis(142)Redis的Cluster的主从复制是如何实现的?
后端
e***58231 小时前
Spring Cloud GateWay搭建
android·前端·后端
程序员西西1 小时前
SpringCloudGateway入门实战
java·spring boot·计算机·程序员·编程
Victor3561 小时前
Redis(143)Redis的Cluster的重新分片是如何实现的?
后端
c***93772 小时前
SpringBoot实现异步调用的方法
java·spring boot·spring
青衫码上行2 小时前
【Java Web学习 | 第15篇】jQuery(万字长文警告)
java·开发语言·前端·学习·jquery
凯子坚持 c3 小时前
Docker 容器实战:从镜像管理到私有仓库构建深度解析
java·docker·eureka
q***01654 小时前
Windows操作系统部署Tomcat详细讲解
java·windows·tomcat
x***13394 小时前
【MyBatisPlus】MyBatisPlus介绍与使用
android·前端·后端