Spring底层(二)Spring IOC容器加载流程原理
一、怎么理解SpringIoc
IOC:Inversion Of Control,即控制反转,是一种设计思想。之前对象又程序员自己new自己创建,现在Spring注入给我们,这样的创建权力被反转了。
所谓控制就是对象的创建、初始化、销毁。
- 创建对象:原来是 new 一个,现在是由 Spring 容器创建。
- 初始化对象:原来是对象自己通过构造器或者 setter 方法给依赖的对象赋值,现在是由 Spring 容器自动注入。
- 销毁对象:原来是直接给对象赋值 null 或做一些销毁操作,现在是 Spring 容器管理生命周期负责销毁对象。
控制反转:“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。
如何设计一个IOC容器:
- Bean的生命周期管理:需要设计Bean的创建、初始化、销毁等生命周期管理机制,可以考虑使用工厂模式和单例模式来实现。
- 依赖注入:需要实现依赖注入的功能,包括属性注入、构造函数注入、方法注入等,可以考虑使用反射机制和XML配置文件来实现。
- Bean的作用域:需要支持多种Bean作用域,比如单例、原型、会话、请求等,可以考虑使用Map来存储不同作用域的Bean实例。
- AOP功能的支持:需要支持AOP功能,可以考虑使用动态代理机制和切面编程来实现。
- 异常处理:需要考虑异常处理机制,包括Bean创建异常、依赖注入异常等,可以考虑使用try-catch机制来处理异常。
- 配置文件加载:需要支持从不同的配置文件中加载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(); }
}