一、概述
IOC中文名为控制反转,这里的反转指的是依赖对象的获得被反转了。为了更明确说明控制反转的含义,我举了一个简单的例子,如下:
假设有一个对象A依赖于对象B:
class A{
//A依赖B
private B b;
//构造函数
public A(B b){
this.b = b;
}
//setter
public void setB(B b){
this.b = b;
}
}
class B{
//类B
}
- 不使用IOC时:当类A依赖类B时,我们首先要先实例化对象B,然后通过A的构造函数将对象B传给A,或者通过A的setter()方法将B传给A。
class Main{
public static void main(String[] args){
B b = new B();
A a = A(b);
}
}
- 使用了IOC时:我们只需要手动实例化A即可,IOC容器会自动帮我们注入A所需要的B。
class A{
//A依赖B
@Autowired
private B b;
//构造函数
public A(){
}
//setter
public void setB(B b){
this.b = b;
}
}
class Main{
public static void main(String[] args){
//属性b会自动注入
A a = new A();
}
}
有了IOC,我们在实例化某个对象的时候,就不需要再关注它依赖哪些类,全部交给IOC容器来管理即可。
二、Spring IOC 加载过程
IOC的加载过程如下图所示:
- 首先,通过
BeanDefinitionReader
读取指定的配置文件生成bean定义信息(BeanDefinition对象
),然后通过BeanFactoryPostProcessor
(BeanFactory后置增强器)对Bean定义信息进行个性化定制,从而得到修改后的BeanDefinition
。 - 得到增强后的
BeanDefiniton
对象后,容器会通过反射的方式将BeanDefinition对象
实例化成具体的bean对象
。
注意:上面提到的BeanFactoryPostProcessor
其实是一个后置增强器的接口,这个后置增强器是可以有多个的,只要我们在多个不同类实现BeanFactoryPostProcessor
接口即可。使用方法如下:
我们可以在postProcessBeanFactory()
方法中对Bean定义信息进行修改。
三、Spring Bean的生命周期
粗略的看,bean的生命周期主要分为以下4个步骤:
但是spring在这4个过程背后做了很多事,细化后的流程图如下:
3.1 实例化
3.1.1 实例化前置处理
实例化前置处理使用的是InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
方法,方法里面两个参数分别是beanClass
和beanName
,顾名思义,就是对在对象实例化之前对bean对象的class信息进行修改或者扩展,以达到我们想要的功能,它的底层是动态代理AOP技术实现的,且是bean生命周期中最先执行的方法。
返回非空:返回值是Object类型,这意味着我们可以返回任何类型的值,由于这个时候目标对象还未实例化,所以这个返回值可以用来代替原本该生成对象的目标对象的实例,也就是说,如果返回了非空的值,那么以后我们需要用到这个bean的时候,拿到的就现在返回的对象了,也就不会去走第二步去实例化对象了。
返回空(null)值:默认也是返回null值的,那么就直接返回,接下来会调用doCreateBean方法来实例化对象。
3.1.2 实例化对象
调用doCreateBean()
方法创建实例对象,用反射技术创建。需要注意的是,这个时候只是将对象实例化了,对象内的属性还未设置。
3.1.3 实例化后置
方法名称: InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(Object bean, String beanName)
在目标对象实例化之后调用,这个时候对象已经被实例化,但是该实例的属性还未被设置,都是null。因为他的返回值是决定要不要调用postProcessPropertyValues方法中的一个因素(因为还有一个因素是mbd.getDependencyCheck())。
返回false :如果该方法返回false,并且不需要check,那么postProcessPropertyValues就会被忽略不执行。
返回true : 如果返回true,postProcessPropertyValues就会被执行。
3.1.4 属性修改
方法名称 :InstantiationAwareBeanPostProcessor.PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
此方法可对属性值进行修改,修改范围包括添加、修改、删除操作,如果实例化后置 postProcessAfterInstantiation()
方法返回false,那么该方法不会被调用。
3.2 初始化
3.2.1 给Bean对象属性赋值
Bean对象属性指的是用spring 的人自定义的bean对象属性,像 User、Student、Teacher 、UserService、IndexService 这类的对象都是自定义bean对象,第5步主要给这类属性进行赋值操作,使用的是 AbstractAutowireCapableBeanFactory.populateBean()
方法进行赋值。
3.2.2 给容器属性赋值
容器属性其实就是容器自带的属性,这些属性都是spring本来就有的。可以肯定的是,它们都是 Aware 接口的实现类,主要有以下实现类,我已经将它们的执行顺序都排列好了,如下图:
3.2.3 初始化前置
方法名称: BeanPostProcessor.postProcessBeforeInitialization()
在每一个 Bean 初始化之前执行的方法(有多少 Bean 调用多少次)
注意 : 启用该方法后,标注了@PostConstruct注解的方法会失效
3.2.4 初始化后置
方法名称: BeanPostProcessor.postProcessAfterInitialization()
在每一个 Bean 初始化之后执行的方法(有多少 Bean 调用多少次)
初始化前置和初始化后置的实现代码如下:
@Component
public class ExtBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 在每一个 Bean 初始化之前执行的方法(有多少 Bean 调用多少次)
// 注意 : 启用该方法后,标注了@PostConstruct注解的方法会失效
System.out.println("初始化前置方法");
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
//在每一个 Bean 初始化之后执行的方法(有多少 Bean 调用多少次)
System.out.println("初始化后置方法");
return null;
}
}
3.2.5 执行初始化方法
初始化方法有三个,分别是 添加了@PostConstruct
注解的方法、实现InitializingBean
接口、在@bean
注解上添加 initMethod
属性。
初始化方法一:@PostConstruct
在bean对象内添加@PostConstruct 注解后即可实现初始化的功能,被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。 有多个则会执行多次;
注意: 如果spring 实现了 BeanPostProcessor接口的postProcessBeforeInitialization() 方法,也就是12的初始后置方法,那么@PostConstruct注解会失效。
示例代码:
// @PostConstruct注解
@Component
public class ExtPostConstruct {
/**
* 被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。如果有多个则会执行多次
* 注意: 如果spring 实现了 BeanPostProcessor接口的postProcessBeforeInitialization方法,该@PostConstruct注解会失效
*/
@PostConstruct
public void init() {
System.out.println("第一个init");
}
// 有多个会执行多次
@PostConstruct
public void init1() {
System.out.println("第二个init方法");
}
}
初始化方法二: InitializingBean.afterPropertiesSet()
spring 初始化方法之一,作用是在BeanFactory完成属性设置之后,执行自定义的初始化行为。
执行顺序:在initMethod之前执行,在@PostConstruct之后执行
代码示例:
@Component
public class ExtInitializingBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// 一个 InitializingBean 执行一次
// spring 初始化方法,作用是在BeanFactory完成属性设置之后,执行自定义的 初始化行为.
// 执行顺序:在initMethod之前执行,在@PostConstruct之后执行
System.out.println("InitializingBean");
}
}
初始化方法二:init-method
bean 配置文件属性 init-method 用于在bean初始化时指定执行方法,用来替代继承 InitializingBean接口,
注意的一点是只有一个类完整的实例被创建出来后,才能走初始化方法。
示例代码,先定义一个类: BeanTest.java ,在类中定义一个初始化方法 initMethod_1()
public class BeanTest {
// 将要执行的初始化方法
public void initMethod_1(){
System.out.println("我是beanTest的init方法");
}
}
xml 配置方式
<bean id="beanTest" class="com.BeanTest" init-method="init"></bean>
注解配置方式
@Component()
public class InitMethod {
// 在@Bean注解上添加initMethod属性,指向类中的 initMethod_1 执行初始化方法
@Bean(initMethod = "initMethod_1")
public BeanTest getBeanTest(){
return new BeanTest();
}
}
3.3 使用中
到这一步,bean对象就已经完全创建好了,是一个完整对象了,并且正在被其他对象使用了。
3.4 销毁
在这里需要先说一下,被spring容器管理的bean默认是单例的,默认在类上面有个 @Scope注解,也就是这样的
@Component()
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
// @Scope(value = "singleton") // 也可以这样写
public class InitMethod {
// methods....
}
如果要设置成多例,只需要把@Scope的属性值改一下就行,就像这样,多例模式也叫原型模式,它底层不是重新创建一个bean对象出来,而是使用深拷贝技术实现的,就是复制一个对象出来进行使用
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
// @Scope(value = "prototype") // 也可以这样写
为什么要介绍单例和多例呢? 因为啊,销毁流程的走向就跟你是单例还是多例有关。
如果是单例模式,会先执行 DisposableBean.destroy()方法,然后在执行 destroy-Method 方法。
3.4.1 DisposableBean.destroy()
单例模式的销毁方式,示例代码
/**
* 销毁方法
*/
@Component
public class ExtDisposableBean implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("我被销毁了");
}
}
3.4.2 destory-method方法
还是拿 第11 个流程的例子来讲,只不过这次我们在@Bean注解里加上 destroyMethod属性,指向销毁方法 :destroyMethod_1()
package com.Spring.Boot.init;
import com.Spring.Boot.init.bean.BeanTest;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component()
public class InitMethod {
// 在@Bean注解上添加initMethod属性,指向类中的 initMethod_1 执行初始化方法
// 在@Bean注解上添加destroyMethod属性,指向类中的 destroyMethod_1 执行销毁方法
@Bean(initMethod = "initMethod_1",destroyMethod = "destroyMethod_1")
public BeanTest getBeanTest(){
return new BeanTest();
}
}
BeanTest.java
package com.Spring.Boot.init.bean;
public class BeanTest {
// 将要执行的初始化方法
public void initMethod_1(){
System.out.println("我是beanTest的init方法");
}
// 将要执行的销毁方法
public void destroyMethod_1(){
System.out.println("我是beanTest的init方法");
}
}
xml的配置方式
<bean id="beanTest" class="com.BeanTest" destroy-method="destroyMethod_1"></bean>
3.4.3 返回bean给用户,剩下的生命周期由用户控制因为
多例模式下,spring无法进行管理,所以将生命周期交给用户控制,用户用完bean对象后,java垃圾处理器会自动将无用的对象进行回收操作。
参考文献:
[1] https://juejin.cn/post/6966158157202587662