当前位置: 首页 > news >正文

Spring底层(二)Spring IOC容器加载流程原理

一、怎么理解SpringIoc

IOC:Inversion Of Control,即控制反转,是一种设计思想。之前对象又程序员自己new自己创建,现在Spring注入给我们,这样的创建权力被反转了。

所谓控制就是对象的创建、初始化、销毁

  1. 创建对象:原来是 new 一个,现在是由 Spring 容器创建
  2. 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入
  3. 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。

控制反转:“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。

如何设计一个IOC容器:

  1. Bean的生命周期管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式来实现。
  2. 依赖注入:需要实现依赖注入的功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
  3. Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求等,可以考虑使用Map来存储不同作用域的Bean实例。
  4. AOP功能的支持:需要支持AOP功能,可以考虑使用动态代理机制和切面编程来实现。
  5. 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
  6. 配置文件加载:需要支持从不同的配置文件中加载Bean的相关信息,可以考虑使用XML、注解或者Java配置类来实现

Spring容器中,存储的主要是Bean对象

二、Spring容器的启动流程

1、第一步创建ApplicationContext的过程

这个容器也叫IOC容器、Spring上下文

ApplicationContext容器:管理Bean对象,通过依赖注入的方式组织Bean之间依赖关系,从而解决业务之间的耦合性

然后通过getBean()方法去获得Bean

2、第二步就是读取配置

xml(ClassPathXmlApplicationContext)、javaconfig(AnnotationConfigApplicationContext )、@Bean、@Configuration

3、第三步是扫描

然后会把配置信息读到BeanDefinition,把扫描的bean变成BeanDefinition,这是一个接口里面就定义了beanClass、scope、lazy-init...通过set方法导入

最终以Map<beanName,beanDefinition类型的对象>形式存储在Spring容器当中

beanDefinition算是Bean的中间状态,封装了一个Bean所需定义信息,后续Bean创建完全由beanDefinition说了算

4、第四步创建Bean

会循环所有的beanDefinition对象,拿到beanName之后通过BeanFactory.getBean(beanName)方法创建Bean

BeanFactory组件:Bean工厂,仅仅用来生产Bean,也是一个接口,用了简单工厂的设计模式

5、检查是否为已经创建的Bean

存储格式:singletonObjects<beanName,Object> 也就是单例池

6、获取不到就创建Bean

实例化:通过beanDefinition的getBeanClass反射调用构造方法完成实例化(推断构造方法)

然后依赖注入变成一个完整的Bean,就是解析​@Autowired,然后返回给getBean()

这里涉及到一个问题:循环依赖

就是A依赖注入B,B依赖注入A,造成循环

具体流程:先通过BeanFactory.getBean(A),然后对A进行实例化,再依赖注入解析B,随后就会去BeanFactory.getBean(B),然后就会去单例池里找,找不到再去newB/实例化B,然后发现B依赖注入里面有个A,又需要去BeanFactory.getBean(A)...最终造成依赖循环。

解决方法:Spring三级缓存

7、初始化 

方法回调:

会把这个Bean放在单例池当中

三、Bean的生命周期 

Spring的启动流程就包含Bean的“生”,另外一篇文章也有提到:

https://blog.csdn.net/2303_80933038/article/details/149421133?fromshare=blogdetail&sharetype=blogdetail&sharerId=149421133&sharerefer=PC&sharesource=2303_80933038&sharefrom=from_linkhttps://blog.csdn.net/2303_80933038/article/details/149421133?fromshare=blogdetail&sharetype=blogdetail&sharerId=149421133&sharerefer=PC&sharesource=2303_80933038&sharefrom=from_link我们来谈谈Bean的“死”

当我们调用容器的.close()方法,所有的Bean都会销毁,销毁的过程中也会调销毁的方法回调,只不过实现的不是InitializingBean接口而是DisposableBean接口

然后调用这个接口下的destory()方法进行销毁

扩展接口:

在初始化过程中,Spring会判断该对象是否实现了BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口,如果实现了,就表示当前对象必须实现该接口中所定义的setBeanName()、setBeanClassLoader()、setBeanFactory()方法,那Spring就会调用这些方法并传入相应的参数(Aware回调),这是因为setBeanName(BeanFactory,beanFactory),得到这个参数就可以做很多事了比如getBean()

Aware回调后,Spring会判断该对象中是否存在某个方法被@PostConstruct注解了,如果存在,Spring会调用当前对象的此方法(初始化前)

还有Bean.PostProcessors.beforeXX和Bean.PostProcessors.afterXX。

在bean加载/销毁前后,如果想实现某些逻辑:

1、使用init-method和destroy-method

<bean id="myBean" class="com.example.MyBeanClass" init-method="init" destroy-method="destroy"/>

然后,在你的Bean类中实现这些方法:

public class MyBeanClass {public void init() {// 初始化逻辑 } public void destroy() {// 销毁逻辑 } 
}

2、实现InitializingBean和DisposableBean接口,实现afterPropertiesSet和destroy方法

import org.springframework.beans.factory.DisposableBean;  
import org.springframework.beans.factory.InitializingBean;  
public class MyBeanClass implements InitializingBean, DisposableBean { @Override  public void afterPropertiesSet() throws Exception {// 初始化逻辑}@Overridepublic void destroy() throws Exception {// 销毁逻辑}
}

3、使用@PostConstruct和@PreDestroy注解

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBeanClass {  @PostConstruct public void init() {// 初始化逻辑 }@PreDestroy  public void destroy() { // 销毁逻辑  } 
}

4、使用@Bean注解的initMethod和destroyMethod属性

@Configuration public class AppConfig { 
@Bean(initMethod = "init", destroyMethod = "destroy") public MyBeanClass myBean() { return new MyBeanClass(); } 
}

http://www.dtcms.com/a/286004.html

相关文章:

  • PermissionError: [Errno 13] Permission denied
  • 复盘爬虫课后练习题
  • 前端学习8:JavaScript数据类型|声明变量|函数定义|函数参数|作用域(多个小练习上手)
  • 【三维重建】LODGE:谷歌DeepMind发布大场景超快3DGS!分层渲染,移动设备均可!
  • CentOS7 内网服务器yum修改
  • 金属伪影校正的双域联合深度学习框架复现
  • blender如何队列渲染多个工程文件的动画?
  • 声画同步!5 个音视频素材适配的网站,创作更和谐
  • 算法学习笔记:29.拓扑排序——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • 前端埋坑之js console.log字符换行后 html没换行问题处理
  • HTML 页面禁止缩放功能
  • javascript 中数组对象操作方法
  • Paimon对比基于消息队列(如Kafka)的传统实时数仓方案的优势
  • Kafka的基本使用
  • 关于在VScode中使用git的一些步骤常用命令及其常见问题:
  • MariaDB 10.4.34 安装配置文档(Windows 版)
  • LLM(Large Language Model)大规模语言模型浅析
  • 第二篇 html5和css3开发基础与应用
  • ElasticSearch Doc Values和Fielddata详解
  • Kotlin序列
  • 外网访问基于 Git 的开源文件管理系统 Gogs
  • CentOS7下的ElasticSearch部署
  • SQL映射文件
  • elasticsearch+logstash+kibana+filebeat实现niginx日志收集(未过滤日志内容)
  • 树的重心相关概念证明
  • MyUI表单VcForm组件文档
  • 组件-多行文本省略-展开收起
  • VMC850立式加工中心Y轴传动机械结构设计cad【7张】三维图+设计说明书
  • 多模态大模型研究每日简报(2025-07-17)
  • 设计循环队列oj题(力口622)