Spring中Bean的生命周期及实例化操作详解
投稿:caobin
前言
Bean的生命周期分为实例化Instantiation 、属性赋值Populate 、初始化Initalization 、销毁Destruction,下面我将从这四个方面深入分享Bean的生命周期。
一、Bean的介绍
我们首先来看看spring的两大核心思想IOC(控制反转),DI(依赖注入)和AOP (面向切面编程)
- IOC(控制反转):是Spring框架的核心思想之一, 主要用于解耦。I0C是指将创建对象的控制权转移给Spring框架进行管理。由Spring框架根据配置文件或注解等元数据,创建bean对象并管理各个bean对象之间的依赖关系。使对象之间形成松散耦合的关系,利于解耦。
- DI(依赖注入):是对IOC概念的不同角度的描述,是指应用程序在运行时,每一个bean对象都依赖IOC容器注入当前bean对象所需要的另外一-个bean对象。 (例如在MyBatis整合Spring时, SqISessionFactoryBean 依赖容器注入-个DataSource数据源)
- IOC容器:IOC容器属于Spring Core模块,是用来创建和管理Bean的地方,以默认单例的方式将bean存储在以ConcurrentHashMap的形式存储了BeanDefinition 对象,该对象封装了Spring 对一个Bean所有信息的定义,包括类名,属性,构造方法参数,依赖,是否延迟加载,是否单例等,之后对Bean的操作都是直接对它进行的。 IOC容器的初始化分三个步骤:
- BeanDefinition的资源定位
- BeanDefinition的资源的载入和解析
- BeanDefinition的注册
- AOP (面向切面编程):Spring AOP基于动态代理实现。 能够将那些与业务无关, 却为业务模块所共同调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块, 这个模块被命名为“ 切面”(Aspect) ,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性;
什么是Bean:
bean是计算机自动生成的类,bean是一个由Spring IoC容器实例化、组装和管理的对象。
也就是说,bean并不是程序员编辑的,而是程序运行时,由spring通过反射生成的
Bean的生命周期:
实例化->属性赋值->初始化->销毁
Bean的作用域:
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
- session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
- global-session: 全局 session 作用域,仅仅在基于 Portlet 的 web 应用中才有意义,Spring5 已经没有了。Portlet 是能够生成语义代码
(例如:HTML)片段的小型 Java Web 插件。它们基于 portlet 容器,可以像 servlet 一样处理 HTTP 请求。
但是,与 servlet 不同,每个 portlet 都有不同的会话。
二、详细过程
实例化和属性赋值分别对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。
可通过查源码的方式发现,他们都在doCreate()方法中,如下:
可通过查源码的方式发现,他们都在doCreate()方法中,
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { // 实例化阶段! instanceWrapper = createBeanInstance(beanName, mbd, args); } // Initialize the bean instance. Object exposedObject = bean; try { // 属性赋值阶段! populateBean(beanName, mbd, instanceWrapper); // 初始化阶段! exposedObject = initializeBean(beanName, exposedObject, mbd); } }
1. Bean的实例化
Spring对Bean进行实例化(相当于 new XXX())
对于 BeanFactory 一般是延迟实例化,就是说调用 getBean 方法才会实例化
但是对于 ApplicationContext ,当容器初始化完成之后,就完成了所有Bean的实例化工作。
实例化的对象被包装在 BeanWrapper 对象中, BeanWrapper 提供了设置对象属性的接口,从而避免了使用反射机制设置属性。
2. InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor 这个接口主要是帮助你在Bean实例化之前做一些操作。
它继承自 BeanPostProcessor 接口,其中 postProcessBeforeInstantiation() 方法是在目标对象实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例。
postProcessPropertyValues 方法是在属性值被设置到目标实例之前调用,可以修改属性的设值。
3. 设置属性(依赖注入)
实例化后的对象被封装到 BeanWrapper 对象中,并且此时对象是一个原生状态,并没有执行依赖注入。
紧接着,Spring根据 BeanDefinition 中的信息进行依赖注入。
并且通过 BeanWrapper 提供的设置属性的接口完成依赖注入。
4. 注入Aware接口
Spring 会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean。
各种各样的Aware接口,其作用就是在对象实例化完成后将Aware接口定义中规定的依赖注入到当前实例中。
比较常见的 ApplicationContextAware 接口,实现了这个接口的类都可以获取到一个 ApplicationContext 对象,当容器中每个对象的实例化过程走到 BeanPostProcessor 前置处理这一步时,容器会检测到之前注册到容器的 ApplicationContextAwareProcessor ,然后就会调用其 postProcessorBeforeInitialization() 方法,检查并设置Aware相关的依赖。
5. BeanPostProcessor的postProcessBeforeInitialzation方法
经过上述步骤后,Bean对象已经被正确构造了,如果你想要对象被使用之前在进行自定义的处理,可以通过 BeanPostProcessor 接口实现。
该接口提供了两个方法
其中 postProcessBeforeInitialzation(Objectbean,StringbeanName) 方法;
当前正在初始化的bean对象会被传递进来,我们就可以对这个Bean做任何处理,这个方法会先于 InitializingBean 执行,因此称为前置处理。
6. InitializingBean与init-method
如果Bean实现了 InitializingBean 接口,Spring将调用它们的 afterPropertiesSet 方法,作用与在配置文件中对Bean使用 init-method 声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。
afterPropertiesSet 方法与前置处理不同的是,由于其没有把Bean对象传进来,因此在这一步没有办法处理对象本身,只能增加一些额外的逻辑。
7.BeanPostProcess的postProcessAfterInitialzation方法
BeanPostProcess 的 postProcessAfterInitialzation(Objectbean,StringbeanName) 方法;当前正在初始化的bean对象会被传递进来,我们就可以对这个bean做任何处理。
这个函数会在 InitializingBean 完成后执行,因此称为后置处理。
8. Bean初始化结束
经过以上的工作以后,Bean的初始化就结束了,Bean将一直驻留在应用上下文中给应用使用,知道应用上下文被销毁。
9. DispostbleBean接口
如果Bean实现了 DispostbleBean 接口,Spring将调用它的 destroy 方法,作用与在配置文件中对Bean使用 destroy-method 属性的作用是一样的,都是在Bean实例销毁前执行的方法。
最后的最后用我多年画工附一张如给大家康康:
到此这篇关于Spring中Bean的生命周期及实例化操作详解的文章就介绍到这了,更多相关Bean的生命周期内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!