- 缩 写
- Bean
- 定 义
- 描述Java的软件组件模型
- 类 型
- 应用程序素材的角色
- 目 的
- 将可以重复使用的软件代码打包
- 应用范围
- 应用于服务器的部件
- 外文名
- Enterprise Java Bean
-
2021-11-12 09:17:49
Spring bean是Spring框架在运行时管理的对象。Spring bean是任何Spring应用程序的基本构建块。你编写的大多数应用程序逻辑代码都将放在Spring bean中。
Spring bean的管理包括:
- 创建一个对象
- 提供依赖项(例如其他bean,配置属性)
- 拦截对象方法调用以提供额外的框架功能
- 销毁一个对象
Spring bean是框架的基本概念。作为Spring的用户,你应该对这个核心抽象有深刻的理解。
如何定义Spring bean?
如你所知,Spring负责创建bean对象。但首先,你需要告诉框架它应该创建哪些对象。
你是怎么做到的?
通过提供bean定义。
Bean定义告诉Spring框架应该将哪些类用作bean。但那还不是全部。Bean定义就像食谱一样。它们还描述了bean的属性。我们将在本文后面讨论属性。但在我们进入之前,让我们关注bean的定义。
可以通过三种不同的方式定义Spring bean:
使用构造型@Component注释(或其衍生物)注释你的类
编写在自定义Java配置类中使用@Bean注释的bean工厂方法
在XML配置文件中声明bean定义
在现代项目中,你将仅使用组件注释和bean工厂方法。如果涉及到XML配置,那么Spring现在主要允许它向后兼容。
bean定义方法之间的选择主要取决于对要用作Spring bean的类的源代码的访问。
Spring bean为@Component
如果你拥有源代码,则通常会直接在类上使用@Component注释。
在运行时,Spring会找到所有使用@Component或其派生类进行注释的类,并将它们用作bean定义。查找带注释的类的过程称为组件扫描。
你可能想知道@Component的衍生物是什么。
@Conpontent衍生物是Spring构造型注释,它们本身用@Component注释。这个事实允许我们用它们代替@Component。
@Component衍生列表包括:
@Service
@Repository
@Controller
注释之间的区别纯粹是信息性的。它们允许你根据通用职责轻松对bean进行分类。你可以使用这些注释将bean类标记为特定应用程序层的成员,Spring框架会将它们全部视为@Components。
使用@Bean作为工厂方法
如果某个类属于某个外部库而你无法使用@Component进行注释,你必须在自定义bean的配置类中使用@ Bean注释创建工厂方法。如果你不想使类依赖于Spring,你也可以将此选项用于你拥有的类。
这是一个带有单个bean工厂方法的示例配置类:
@Configuration class MyConfigurationClass { @Bean public NotMyClass notMyClass() { return new NotMyClass(); } }
Spring使用工厂方法在运行时创建实际对象。如你所见,该方法只返回一个类的新实例。如果某个类属于某个外部库而你无法使用@Component进行注释,则使用@Bean创建工厂方法是唯一的选择。
@Configuration注释也来自Spring。实际上,它是@Component的衍生物,但具有特殊用途。注释将类标记为@Bean定义的容器。你可以在单个配置类中编写多个工厂方法。
Spring bean属性
此时,你已经知道如何将类标记为Spring bean。下一步是学习如何自定义bean的功能。
无论你选择哪种bean定义方法,它们都允许描述同一组bean属性。属性提供有关Spring应如何创建对象的详细信息。
Spring bean属性列表包括:
- 类
- 名称
- 依赖
- 范围
- 初始化模式
- 初始化回调
- 破坏回调
为了简化bean定义,Spring为几乎所有属性提供了默认值。但是,了解如何自定义默认值非常重要。让我们逐个研究Spring bean属性。
1.Bean类
创建bean定义时,将其与应用程序中的单个具体类连接。这个类本身是bean的主要属性。
当Spring查找依赖项时,class属性是bean的默认标识符。这是否意味着你不能为单个类提供多个bean定义?不,这是可能的。但在这种情况下,为避免歧义,你应该使用另一个bean标识符:一个name名称。
2. Bean名称name
Spring bean名称是Spring用于标识bean的自定义字符串。与bean类不同,名称在整个应用程序中必须是唯一的。你不能定义两个具有相同名称的bean,即使它们的类型不同。
幸运的是,你不必为你创建的每个bean设置名称。Spring在运行时为其内部使用生成名称。除非你想按名称识别bean,否则可以安全地使用默认设置。
你需要使用bean名称的主要情况是为使用@Bean批注定义的同一个类提供了几个bean。在这种情况下,名称允许你标识要用作另一个bean的依赖项的特定实例。
3. 如何命名Spring bean?
使用@Bean批注的name属性。这是两个具有相同类型的bean的示例。
@Configuration class MyConfigurationClass { @Bean(name = "myBeanClass") MyBeanClass myBeanClass() { return new MyBeanClass(); } @Bean(name = "anotherMyBeanClass") MyBeanClass anotherMyBeanClass() { return new MyBeanClass(); } }
实际上,你不经常定义bean名称。对于单个类具有多个bean是相当罕见的情况。然而,如果能了解命名bean的各种可能性也是不错的。
4. Bean依赖项
用作bean的对象可以使用其他bean来执行其作业。当Spring创建一个定义某些依赖项的对象时,框架需要首先创建这些依赖项。这些依赖项也可以有自己的依赖项。
在面向对象的应用程序中,我们通常使用相关对象的巨大图表。幸运的是,我们不必考虑如何构建此图。我们不必考虑应该创建对象的顺序。Spring为我们做了所有这些。
Spring对你的唯一期望是特定bean的依赖项列表。
5.如何定义bean依赖?
Bean依赖关系定义是一个复杂的主题,值得单独一篇文章。请考虑以下段落作为该主题的介绍。
当你有一个用@Component标记的类并且只有一个构造函数时,Spring使用构造函数参数列表作为必需依赖项列表。默认情况下,框架使用构造函数参数类型来提供适当的对象。
@Component class BeanWithDependency { private final MyBeanClass beanClass; BeanWithDependency(MyBeanClass beanClass) { this.beanClass = beanClass; } }
过去,我们在构造函数上使用@Autowired注释。但是,从Spring 4.3开始,如果只有一个构造函数,则不是强制性的。但是,如果bean类定义了多个构造函数,则应使用@Autowired标记一个。这样Spring知道哪个构造函数包含bean依赖项列表。
出于同样的原因,bean工厂方法可以定义其依赖关系。Spring使用适当的对象调用方法。
@Bean BeanWithDependency beanWithOptionalDependency(MyBeanClass beanClass) { return new BeanWithDependency(beanClass); }
6. Bean作用域
Spring bean的范围定义了框架在运行时创建的特定类的实例数。作用域还描述了创建新对象的条件。
Spring为你的bean提供了几个作用域。框架的核心有两个:
- 单例 - 单个实例
- 原型 - 多个实例
此外,Spring还附带了专门用于Web应用程序的bean作用域:
- 请求
- 会话
- 全局会话
- 应用级别Application
所有bean的默认作用域是单例。当bean具有单例作用域时,Spring只创建一个实例并在整个应用程序中共享它。单例是无状态对象的完美选择。如今,我们应用程序中的绝大多数bean都是无状态单例。
另一方面,如果对象包含状态,则应考虑其他作用域。要选择正确的一个,你应该问自己框架应该将该状态保留在内存中多长时间。但这是另一篇文章。
7. 如何设置作用域?
无论是使用@Component直接注释类还是使用@Bean创建工厂方法,该方法都是相同的。使用@Scope批注及其字符串属性选择范围。
@Component @Scope("prototype") class MyPrototypeClass { //... } @Bean @Scope("prototype") MyPrototypeClass myPrototypeClass() { return new MyPrototypeClass(); }
- 更重要的是,对于Web作用域,Spring附带了额外的别名注释。你可以使用这些注释代替@Scope:
- @RequestScope
- @SessionScope
- @ApplicationScope
Bean初始化模式
当你的应用程序启动时,Spring会在启动时会立即创建所有单例bean。此默认行为允许我们快速检测bean定义中的错误。另一方面,立即性eager bean初始化会使应用程序的启动变慢。
幸运的是,你可以将bean的创建延迟到实际需要的时刻。你可以使用@Lazy注释执行此操作。
@Component @Lazy class MyLazyClass { //... }
Bean初始化回调
一旦Spring根据bean定义创建新实例,你可能希望运行一些对象初始化逻辑。
如果此逻辑不依赖于框架,则可以在对象的构造函数中运行它。但是,为了确保在Spring初始化对象之后运行逻辑(例如,在可选的依赖注入之后),你应该使用初始化回调。
如何设置bean初始化回调?
如果使用@Component定义bean ,则有两个选项:
使bean类实现InitializingBean。接口将强制你实现初始化方法。
编写自定义初始化方法并使用javax @PostContruct注释进行标记。
在这两种情况下,Spring都会为你运行初始化回调。
用工厂方法定义的bean怎么样?
你可以使用@Bean及其名为initMethod的属性设置初始化回调。该属性需要一个具有初始化方法名称的字符串。
@Bean(initMethod = "someInitMethodName") MySpringBeanClass meBeanClass() { return new MySpringBeanClass(); }
有趣的是,用作初始化回调的方法可以具有私有访问控制。Spring使用反射机制来调用该方法。
Bean销毁回调
与初始化回调类似,你可以定义Spring销毁bean时应调用的方法。Predestroy回调的使用要少得多,但要注意它们的存在是很好的。
如何设置bean销毁回调?
同样,如果你可以访问bean类的源代码,则可以使用以下两个选项之一:
实现DisposableBean接口。Spring使用其唯一的方法进行销毁回调。
- 编写自定义方法并使用Javax API中的@PreDestroy注释它。
- 对于工厂方法,使用@Bean批注及其destroyMethod属性。
@Bean(name = "myBeanClass", destroyMethod = "cleanUpMethod") MySpringBeanClass meBeanClass() { return new MySpringBeanClass(); }
Spring如何从bean定义创建对象?
当你启动Spring应用程序时,框架首先会创建一个名为ApplicationContext的特殊对象。ApplicationContext,也称为控制反转(IoC)容器,是框架的核心。
ApplicationContext是存在bean对象的容器。
上下文负责检测和读取Spring bean定义。一旦ApplicationContext加载了bean定义,它就可以根据提供的bean属性及其依赖关系开始为应用程序创建bean对象。
更多相关内容 -
Spring Bean生命周期,好像人的一生。。
2022-03-15 11:43:41Spring原来是一个社会,Bean就是身不由己的“社会人”……大家好,我是老三,上节我们手撸了一个简单的IOC容器五分钟,手撸一个Spring容器!,这节我们来看一看Spring中Bean的生命周期,我发现,和人的一生真的很像。
简单说说IoC和Bean
IoC,控制反转,想必大家都知道,所谓的控制反转,就是把new对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。
Bean,也不是什么新鲜玩意儿,它们就是一帮身不由己的Java对象,生命周期受到容器控制。
Bean生命周期和人生
Bean生命周期四大阶段
我们知道,bean的作用域有好几种,这篇文章只讨论完全被IoC容器控制的单例Bean。
对于普通的Java对象来说,它们的生命周期就是:
- 实例化
- 对象不再被使用时通过垃圾回收机制进行回收
这就像是生活在大自然里的动物,悄然出生,悄然死亡。
而对于Spring Bean的生命周期来说,可以分为四个阶段,其中初始化完成之后,就代表这个Bean可以使用了:
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
人和动物不一样,存在非常复杂的社会。
我们来看看社会里的人,一生要经历哪些阶段,是不是和Bean的生命周期很像呢?
- 出生:作为一个自然人降临在这个世界
- 登记:登记身份证号,姓名,正式成为人类社会的一份子
- 成长:接受教育,成为对社会有用的人
- 工作:为社会创造价值
- 死亡:人死如灯灭,不过人这盏灯灭了,还要把灯台埋起来
Bean实例化的时机也分为两种,BeanFactory管理的Bean是在使用到Bean的时候才会实例化Bean,ApplicantContext管理的Bean在容器初始化的时候就回完成Bean实例化。
BeanFactory就是相对不那么健全的原始一些的社会,ApplicantContext是发达健全的现代社会。
Bean详细生命周期
我们讲到了Bean容器四个阶段,会有一些容器级的方法,进行前置和后置的处理,比如InstantiationAwareBeanPostProcessor、BeanPostProcessor接口方法。这些方法独立于Bean之外,并且会注册到Spring容器中,在Spring容器创建Bean的时候,进行一些处理。
这就好像,孩子出生之前,需要做一些准备,比如备孕、养胎、备产什么的,出生之后,需要做一些护理。孩子上学前后,也需要做一些学籍的管理。
那么有了各种各样的扩展之后,我们再接着看看Bean的详细的生命周期。首先,我们面临一个问题——Bean的生命周期从什么时候开始的呢?
上面写了,Bean实例化前后,可以进行一些处理,但是如果从Bean实例化前算开始,那么就要追溯到容器的初始化、beanDefiinition的加载开始。
所以这篇文章里,我们取生命周期直接从Bean实例化开始,但是大家也要知道,Bean实例化前后,可以使用后处理器进行处理,例如BeanFactoryPostProcessor、InstantiationAwareBeanPostProcessor。
大家也不要困扰,就像计算人生的起点,是从母亲怀孕算起,还是从孩子出生算起?我们这里取了出生开始而已。
- 实例化:第 1 步,实例化一个 Bean 对象
- 属性赋值:第 2 步,为 Bean 设置相关属性和依赖
- 初始化:初始化的阶段的步骤比较多,5、6步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean就可以被使用了
- 销毁:第 8~10步,第8步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第9、10步真正销毁 Bean 时再执行相应的方法
我们发现Bean生命周期的详细过程,是不是也像人生的历程,出生、登记,不过是很短的事情。慢慢长大成人,要经历人生的四分之一,而成长,来源于教育,不管是学校的还是社会的,接受教育前,要登记学籍,上学的时候,自己还要努力……,到最后,要发一纸薄薄的文凭,标志着我们成为可以捶打的“社会人”。
然后,为社会奉献四十年。最后老去,离世。不过Bean的世界,没有退休——当然,也许,人的世界也没有退休。
我们发现中间的一些扩展过程也可以分四类:
-
一:获取社会资源/Aware接口:Aware接口的作用是让Bean能拿到容器的一些资源,例如BeanNameAware可以拿到BeanName。就好像上学之前,要取一个学名——不知道多少人上学之前不知道自己大名叫什么,是吧?二毛。
-
二:必备各种手续和证/后处理器:在Bean的生命周期里,会有一些后处理器,它们的作用就是进行一些前置和后置的处理,就像上学之前,需要登记学籍,上学之后,会拿到毕业证。
-
三:个人选择/生命周期接口:人可能无法选择如何出生,但也许可以选择如何活着和如何死去,InitializingBean和DisposableBean 接口就是用来定义初始化方法和销毁方法的。
-
四:主观能动/配置生命周期方法:环境影响人,人也在影响环境,成长的时候认真努力,衰亡的时候也可以豁达乐观。可以通过配置文件,自定义初始化和销毁方法。
PersonBean的一生
话不多说,接下来我们拿一个例子,来看看PersonBean的一生,我们先来看一下它的流程!
用文字描述一下这个过程:
- Bean容器在配置文件中找到Person Bean的定义,这个可以说是妈妈怀上了。
- Bean容器使用Java 反射API创建Bean的实例,孩子出生了。
- Person声明了属性no、name,它们会被设置,相当于注册身份证号和姓名。如果属性本身是Bean,则将对其进行解析和设置。
- Person类实现了
BeanNameAware
接口,通过传递Bean的名称来调用setBeanName()
方法,相当于起个学名。 - Person类实现了
BeanFactoryAware
接口,通过传递BeanFactory对象的实例来调用setBeanFactory()
方法,就像是选了一个学校。 - PersonBean实现了BeanPostProcessor接口,在初始化之前调用用
postProcessBeforeInitialization()
方法,相当于入学报名。 - PersonBean类实现了
InitializingBean
接口,在设置了配置文件中定义的所有Bean属性后,调用afterPropertiesSet()
方法,就像是入学登记。 - 配置文件中的Bean定义包含
init-method
属性,该属性的值将解析为Person类中的方法名称,初始化的时候会调用这个方法,成长不是走个流程,还需要自己不断努力。 - Bean Factory对象如果附加了Bean 后置处理器,就会调用
postProcessAfterInitialization()
方法,毕业了,总得拿个证。 - Person类实现了
DisposableBean
接口,则当Application不再需要Bean引用时,将调用destroy()
方法,简单说,就是人挂了。 - 配置文件中的Person Bean定义包含
destroy-method
属性,所以会调用Person类中的相应方法定义,相当于选好地儿,埋了。
我们来看看代码!
PersonBean类
创建一个PersonBean,让它实现几个特殊的接口,我们来观察一下它的生命周期的流转。
public class PersonBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean { /** * 身份证号 */ private Integer no; /** * 姓名 */ private String name; public PersonBean() { System.out.println("1.调用构造方法:我出生了!"); } public Integer getNo() { return no; } public void setNo(Integer no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("2.设置属性:我的名字叫"+name); } @Override public void setBeanName(String s) { System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("6.InitializingBean#afterPropertiesSet方法:入学登记"); } public void init() { System.out.println("7.自定义init方法:努力上学ing"); } @Override public void destroy() throws Exception { System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了"); } public void destroyMethod() { System.out.println("10.自定义destroy方法:睡了,别想叫醒我"); } public void work(){ System.out.println("Bean使用中:工作,只有对社会没有用的人才放假。。"); } }
- 实现了InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean四个接口
- 定义了no、name两个属性和对应的getter、setter方法
- 定义了一个实例方法work
MyBeanPostProcessor
自定义了一个后处理器MyBeanPostProcessor:
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!"); return bean; } }
配置文件
定义一个配置文件spring-config.xml:
- 使用setter注入
- 定义init-method和destroy-method
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="myBeanPostProcessor" class="cn.fighter3.spring.life.MyBeanPostProcessor" /> <bean name="personBean" class="cn.fighter3.spring.life.PersonBean" init-method="init" destroy-method="destroyMethod"> <property name="idNo" value= "80669865"/> <property name="name" value="张铁钢" /> </bean> </beans>
测试
最后测试一下,观察PersonBean的生命周期的流转:
public class Main { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); PersonBean personBean = (PersonBean) context.getBean("personBean"); personBean.work(); ((ClassPathXmlApplicationContext) context).destroy(); } }
运行结果:
1.调用构造方法:我出生了! 2.设置属性:我的名字叫张铁钢 3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名 4.调用BeanFactoryAware#setBeanFactory方法:选好学校了 5.BeanPostProcessor#postProcessBeforeInitialization方法:到学校报名啦 6.InitializingBean#afterPropertiesSet方法:入学登记 7.自定义init方法:努力上学ing 8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦! Bean使用中:工作,只有对社会没有用的人才放假。。 9.DisposableBean#destroy方法:平淡的一生落幕了 10.自定义destroy方法:睡了,别想叫醒我
看看,是不是和我们图中的流程一致。
这篇文章就不带大家跟进更多的源码了,如果大家对源码级别的Bean的生命周期感兴趣,可以看看
AbstractApplicationContext
类里的refresh
方法,这个方法是AplicationContext容器初始化的关键点。在这个方法里,调用了finishBeanFactoryInitialization
方法,这个方法里调用了getBean
方法,getBean
方法里调用了AbstractBeanFactory
的getBean
方法。最终经过一阵七拐八绕,到达了我们的目标——Bean创建的方法:
doGetBean
方法,在这个方法里可以看到Bean的实例化,赋值、初始化的过程,至于最终的销毁,可以看看ConfigurableApplicationContext#close()
。结语
到这,这篇Bean的生命周期文章就走向destory了,自定义destory方法——回顾一下这篇文章的“一生”。
- Bean的生命周期大致可以分为四个阶段:实例化、属性赋值、初始化、销毁,对应人生的出生、登记、成长、离世。
- Bean生命周期中可以有很多扩展,就像人生的走向,会受很多影响,社会的环境、自身的选择、自己的努力。
参考:
[1]. 《Spring揭秘》
[2]. Spring官网
[3].《精通Spring4.X企业应用开发实战》
[4] .Spring Bean 生命周期 (实例结合源码彻底讲透)
干货文章首发,欢迎关注👇👇👇 -
一文读懂 Spring Bean 的生命周期
2021-07-05 23:02:56今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,...欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
一、前言
今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个核心概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不知道跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不知道意思了,太绕了。
本文老周试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有继续研究学习的想法,那我写此文的目的也就达到了。
我们讲 Spring Bean 的生命周期之前先来了解两个概念:
1.1 什么是 Bean
我们来看下 Spring Framework 的官方文档:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
简而言之,bean 是由 Spring IoC 容器实例化、组装和管理的对象。
1.2 什么是 Spring Bean 的生命周期
对于普通的 Java 对象,当 new 的时候创建对象,然后该对象就能够使用了。一旦该对象不再被使用,则由 Java 自动进行垃圾回收。
而 Spring 中的对象是 bean,bean 和普通的 Java 对象没啥大的区别,只不过 Spring 不再自己去 new 对象了,而是由 IoC 容器去帮助我们实例化对象并且管理它,我们需要哪个对象,去问 IoC 容器要即可。IoC 其实就是解决对象之间的耦合问题,Spring Bean 的生命周期完全由容器控制。
二、Spring Bean 的生命周期
这里老周必须要提一下,这里我们说的 Spring Bean 的生命周期主要指的是 singleton bean,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。
我们也来复习下 Spring 中的 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 都有不同的会话。
我们知道对于普通的 Java 对象来说,它们的生命周期就是:
- 实例化
- 该对象不再被使用时通过垃圾回收机制进行回收
而对于 Spring Bean 的生命周期来说:
- 实例化 Instantiation
- 属性赋值 Populate
- 初始化 Initialization
- 销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁
只有四个步骤,这样拆解的话是不是感觉也不难?不像其他人写的那样直接一上来就各种 BeanPostProcessor、BeanFactoryPostProcessor 全部怼进流程里去,别说读者看着头大,自己写的可能短时间内还记得流程,隔个一段时间,你可能都不知道自己写了个啥。
本来老周想通过 Bean 创建流程入口
AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 处带大家跟一下源码,想了想还是不带入过多的代码进来,直接给到最终的主要逻辑。protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 实例化阶段 instanceWrapper = this.createBeanInstance(beanName, mbd, args); } ... Object exposedObject = bean; try { // 属性赋值阶段 this.populateBean(beanName, mbd, instanceWrapper); // 初始化阶段 exposedObject = this.initializeBean(beanName, exposedObject, mbd); } catch (Throwable var18) { ... } ... }
至于销毁,是在容器关闭时调用的,详见
ConfigurableApplicationContext#close()
是不是很清爽了?至于 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的类,在老周看来,只不过是对主流程四个步骤的一系列扩展点而已。
三、Spring Bean 的生命周期的扩展点
Spring Bean 的生命周期的扩展点超级多,老周这里不可能全部列出来,只说核心的扩展点。这也就是为什么 Spring 的扩展性很好的原因,开了很多的口子,尽可能让某个功能高内聚松耦合,用户需要哪个功能就用哪个,而不是直接来一个大而全的东西。
3.1 Bean 自身的方法
比如构造函数、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就对应着上文说的实例化 -> 属性赋值 -> 初始化 -> 销毁四个阶段。
3.2 容器级的方法(BeanPostProcessor 一系列接口)主要是后处理器方法,比如下图的
InstantiationAwareBeanPostProcessor
、BeanPostProcessor
接口方法。这些接口的实现类是独立于 Bean 的,并且会注册到 Spring 容器中。在 Spring 容器创建任何 Bean 的时候,这些后处理器都会发生作用。
3.2.1 InstantiationAwareBeanPostProcessor 源码分析我们翻一下源码发现 InstantiationAwareBeanPostProcessor 是继承了 BeanPostProcessor
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
调用点
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
返回值:如果返回的不为null,那么后续的Bean的创建流程【实例化、初始化afterProperties】都不会执行,而是直接使用返回的快捷Bean,此时的正常执行顺序如下:
InstantiationAwareBeanPostProcessor接口中的postProcessBeforeInstantiation,在实例化之前调用。
BeanPostProcessor接口中的postProcessAfterInitialization,在实例化之后调用。
总之,postProcessBeforeInstantiation 在 doCreateBean 之前调用,也就是在 bean 实例化之前调用的,英文源码注释解释道该方法的返回值会替换原本的 Bean 作为代理,这也是 AOP 等功能实现的关键点。InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
调用点
boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
正常情况下在实例化之后在执行populateBean之前调用
返回值:如果有指定的bean的时候返回false,那么后续的属性填充和属性依赖注入【populateBean】将不会执行,同时后续的postProcessPropertyValues将不会执行,但是初始化和BeanPostProcessor的仍然会执行。public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
实例化之后调用,在方法applyPropertyValues【属性填充】之前
返回值:如果返回null,那么将不会进行后续的属性填充,比如依赖注入等,如果返回的pvs额外的添加了属性,那么后续会填充到该类对应的属性中。
pvs:PropertyValues对象,用于封装指定类的对象,简单来说就是PropertyValue的集合,里面相当于以key-value形式存放类的属性和值。
pds:PropertyDescriptor对象数组,PropertyDescriptor相当于存储类的属性,不过可以调用set,get方法设置和获取对应属性的值。
3.2.2 BeanPostProcessor 源码分析org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
进入初始化接口:
我们先来看org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
- 首先获取到所有的后置处理器 getBeanPostProcessors()
- 在 for 循环中依次调用后置处理器的方法
processor.postProcessBeforeInitialization(result, beanName);
- 进入 postProcessBeforeInitialization 方法
org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization
进入
invokeAwareInterfaces(bean);
方法,当前 bean 实现了 ApplicationContextAware 接口。ApplicationContextAwareProcessor#postProcessBeforeInitialization
首先判断此 bean 是不是各种的Aware,如果是它列举的那几个 Aware 就获取 Bean 工厂的权限,可以向容器中导入相关的上下文环境,目的是为了 Bean 实例能够获取到相关的上下文,如果不是它列举的几个 Aware,那就调用invokeAwareInterfaces(bean)
,向容器中添加相关接口的上下文环境。
3.3 工厂后处理器方法(BeanFactoryProcessor 一系列接口)
包括
AspectJWeavingEnabler
、CustomAutowireConfigurer
、ConfigurationClassPostProcessor
等。这些都是 Spring 框架中已经实现好的 BeanFactoryPostProcessor,用来实现某些特定的功能。我们知道 Spring IoC 容器初始化的关键环节就在
org.springframework.context.support.AbstractApplicationContext#refresh
方法中 ,容器创建的主体流程都在这个方法里面,这个方法是真的重要!!!对于工厂后处理器方法老周这里直接带你看
invokeBeanFactoryPostProcessors(beanFactory);
方法,这个方法处理的是BeanFactoryPostProcessor
接口的 Bean。调用方法如下:
跟到最重要的方法里去,代码虽长,但逻辑中规中矩。BeanFactoryPostProcessor
:一切处理 BeanFactory 的父接口
BeanDefinitionRegistryPostProcessor
:实现了 BeanFactoryPostProcessor 接口的接口
流程说明:- 调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(registry) 方法。参数 beanFactoryPostProcessors 传入的优先处理掉。然后获取容器注册的,对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
- 调用 BeanFactoryPostProcessor#postProcessBeanFactory(beanFactory) 方法。备注:BeanDefinitionRegistryPostProcessor 属于 BeanFactoryPostProcessor 子接口。先处理属于 BeanDefinitionRegistryPostProcessor 接口实例的 postProcessBeanFactory(beanFactory) 方法,然后获取容器注册的。对于这些 Bean 按照 PriorityOrdered 接口、Ordered、没有排序接口的实例分别进行处理。
3.4 Bean 级生命周期方法
可以理解为 Bean 类直接实现接口的方法,比如
BeanNameAware
、BeanFactoryAware
、ApplicationContextAware
、InitializingBean
、DisposableBean
等方法,这些方法只对当前 Bean 生效。3.4.1 Aware 类型的接口
Aware 类型的接口的作用就是让我们能够拿到 Spring 容器中的一些资源。基本都能够见名知意,Aware 之前的名字就是可以拿到什么资源,例如 BeanNameAware 可以拿到 BeanName,以此类推。调用时机需要注意:所有的 Aware 方法都是
在初始化阶段之前调用的
。Aware 接口众多,这里同样通过分类的方式帮助大家记忆。Aware 接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是 Aware 接口的执行顺序,能够见名知意的接口不再解释。
Aware Group1
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
Aware Group2
EnvironmentAware
EmbeddedValueResolverAware
这个知道的人可能不多,实现该接口能够获取 Spring EL 解析器,用户的自定义注解需要支持 SPEL 表达式的时候可以使用,非常方便。ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的 ApplicationContext 对象,因为 ApplicationContext 是一个复合接口,如下:
Aware 调用时机源码分析
可以看到并不是所有的 Aware 接口都使用同样的方式调用。Bean××Aware 都是在代码中直接调用的,而 ApplicationContext 相关的 Aware 都是通过 BeanPostProcessor#postProcessBeforeInitialization() 实现的。感兴趣的可以自己看一下 ApplicationContextAwareProcessor 这个类的源码,就是判断当前创建的 Bean 是否实现了相关的 Aware 方法,如果实现了会调用回调方法将资源传递给 Bean。BeanPostProcessor 的调用时机也能在这里体现,包围住 invokeInitMethods 方法,也就说明了在初始化阶段的前后执行。
关于 Aware 接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。
3.4.2 生命周期接口
至于剩下的两个生命周期接口就很简单了,实例化和属性赋值都是 Spring 帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。
InitializingBean
对应生命周期的初始化阶段,在上面源码的invokeInitMethods(beanName, wrappedBean, mbd);
方法中调用。
有一点需要注意,因为 Aware 方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用 Aware 接口获取的资源,这也是我们自定义扩展 Spring 的常用方式。
除了实现 InitializingBean 接口之外还能通过注解或者 xml 配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。DisposableBean
类似于 InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()
方法作为入口,实现是通过循环取所有实现了 DisposableBean 接口的 Bean 然后调用其 destroy() 方法,感兴趣的可以自行跟一下源码。
3.5 Spring Bean 生命周期流程图
四、常用接口说明
4.1 BeanNameAware
该接口只有一个方法
setBeanName(String name)
,用来获取 bean 的id 或者 name
。4.2 BeanFactoryAware
该接口只有一个方法
setBeanFactory(BeanFactory beanFactory)
,用来获取当前环境中的 BeanFactory
。4.3 ApplicationContextAware
该接口只有一个方法
setApplicationContext(ApplicationContext applicationContext)
,用来获取当前环境中的 ApplicationContext
。4.4 InitializingBean
该接口只有一个方法
afterPropertiesSet()
,在属性注入完成后调用
。4.5 DisposableBean
该接口只有一个方法
destroy()
,在容器销毁的时候调用,在用户指定的 destroy-method 之前调用
。4.6 BeanPostProcessor
该接口有两个方法:
postProcessBeforeInitialization(Object bean, String beanName)
:在初始化之前
调用此方法postProcessAfterInitialization(Object bean, String beanName)
:在初始化之后
调用此方法
通过方法签名我们可以知道,我们可以通过 beanName 来筛选出我们需要进行个性化定制的 bean。
4.7 InstantiationAwareBeanPostProcessor
该类是 BeanPostProcessor 的子接口,常用的有如下三个方法:
postProcessBeforeInstantiation(Class beanClass, String beanName)
:在bean实例化之前
调用postProcessProperties(PropertyValues pvs, Object bean, String beanName)
:在bean实例化之后、设置属性前
调用postProcessAfterInstantiation(Class beanClass, String beanName)
:在bean实例化之后
调用
五、代码演示
思路:创建一个类 UserBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。
5.1 UserBean 类
@Component public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware { private int id; private String name; public UserBean(int id, String name) { this.id = id; this.name = name; System.out.println("2. 调用构造函数"); } public int getId() { return id; } public void setId(int id) { this.id = id; System.out.println("5. 属性注入 id"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("5. 属性注入 name"); } @Override public void setBeanName(String name) { System.out.println("6. 调用 BeanNameAware.setBeanName() 方法"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { UserBean userBean = (UserBean) applicationContext.getBean("userBean"); System.out.println(userBean); System.out.println("7. 调用 BeanNameAware.setBeanName() 方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("9. 调用 InitializingBean.afterPropertiesSet() 方法"); } public void myInit() { System.out.println("10. 调用 init-method 方法"); } @Override public void destroy() throws Exception { System.out.println("12. 调用 DisposableBean.destroy() 方法"); } public void myDestroy() { System.out.println("13. 调用 destroy-method 方法"); } @Override public String toString() { return "UserBean{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
5.2 InstantiationAwareBeanPostProcessor 接口实现类
@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("1. 调用 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法"); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { UserBean userBean = (UserBean) bean; System.out.println("3. 调用 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法"); System.out.println(userBean); } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("4. 调用 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法"); } return null; } }
5.3 BeanPostProcessor 接口实现类
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("8. 调用 BeanPostProcessor.postProcessBeforeInitialization() 方法"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("11. 调用 BeanPostProcessor.postProcessAfterInitialization() 方法"); } return bean; } }
5.4 BeanFactoryPostProcessor 接口实现类
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("0. 调用 BeanFactoryPostProcessor.postProcessBeanFactory() 方法"); } }
5.5 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd "> <bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" /> <bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy"> <!-- 构造函数注入 --> <constructor-arg index="0" type="int"> <value>1</value> </constructor-arg> <constructor-arg index="1" type="java.lang.String"> <value>微信公众号【老周聊架构】</value> </constructor-arg> <!-- setter方法注入 --> <property name="id" value="2"/> <property name="name" value="riemann"/> </bean> <bean class="com.riemann.test.MyBeanPostProcessor" /> <bean class="com.riemann.test.MyBeanFactoryPostProcessor" /> </beans>
5.6 测试类
public class BeanLifeCycleTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); UserBean user = (UserBean) applicationContext.getBean("userBean"); ((AbstractApplicationContext) applicationContext).close(); } }
5.7 控制台结果打印
-
详解Spring的Bean销毁
2021-10-13 17:21:27protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { …… registerDisposableBeanIfNecessary(beanName, bean, mbd); ……一、注册Bean销毁逻辑
Bean的生命周期中,在完成了Bean的创建之后,会注册Bean销毁的逻辑
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { …… registerDisposableBeanIfNecessary(beanName, bean, mbd); …… }
在注册Bean销毁逻辑时,首先需要通过!mbd.isPrototype()保证该Bean不是原型Bean,因为Spring容器并不会缓存原型Bean,就没有销毁一说
接着通过requiresDestruction()判断该Bean是否需要销毁,对需要销毁的Bean通过适配器模式生成DisposableBeanAdapter对象,最后调用registerDisposableBean()将DisposableBeanAdapter对象放入disposableBeans缓存中,当Spring容器关闭的时候,可以直接从该缓存中取出销毁调用,调用它们的销毁方法
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null); if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) { if (mbd.isSingleton()) { // Register a DisposableBean implementation that performs all destruction // work for the given bean: DestructionAwareBeanPostProcessors, // DisposableBean interface, custom destroy method. registerDisposableBean(beanName, new DisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); } …… } }
1.1 判断当前Bean是否需要销毁
requiresDestruction()方法源码如下:
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors( bean, getBeanPostProcessorCache().destructionAware)))); }
-
判断当前Bean是否有销毁方法
如果当前Bean继承了DisposableBean或AutoCloseable接口,重写接口中的destroy()和close()方法,这两个方法都是销毁方法
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) { if (bean instanceof DisposableBean || bean instanceof AutoCloseable) { return true; } return inferDestroyMethodIfNecessary(bean, beanDefinition) != null; }
如果没有继承这两个接口,则判断当前Bean的RootBeanDefinition是否设置销毁方法名为"(inferred)"
设置初始化和销毁方法名都是在Bean实例化后BeanDefinition的后置处理,即MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()中完成的
如果在BeanDefinition的后置处理中设置销毁方法名为"(inferred)",则会将该Bean中close()和shutdown()作为销毁方法(前提是Bean里面有这两个方法)
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) { String destroyMethodName = beanDefinition.resolvedDestroyMethodName; if (destroyMethodName == null) { destroyMethodName = beanDefinition.getDestroyMethodName(); // if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) { destroyMethodName = null; if (!(bean instanceof DisposableBean)) { try { destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName(); } catch (NoSuchMethodException ex) { destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName(); } } } beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : ""); } return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null); }
-
判断是否有Bean实现了DestructionAwareBeanPostProcessor接口且requiresDestruction()方法返回True
DestructionAwareBeanPostProcessor接口主要用于Bean销毁的,其中的requiresDestruction()判断Bean是否需要销毁,而postProcessBeforeDestruction()实现具体的销毁逻辑
在《创建Bean》的最后面,讲到了@PreDestroy注解,它主要用于定义销毁方法(被该注解修饰的方法都是销毁方法),而该注解的扫描的是在InitDestroyAnnotationBeanPostProcessor类中完成,InitDestroyAnnotationBeanPostProcessor实现了DestructionAwareBeanPostProcessor接口,它会缓存每个Bean以及它的父类哪些方法是被@PreDestroy修饰的
hasDestructionAwareBeanPostProcessors()主要判断是否缓存的有实现了DestructionAwareBeanPostProcessor的Bean
protected boolean hasDestructionAwareBeanPostProcessors() { return !getBeanPostProcessorCache().destructionAware.isEmpty(); }
然后调用DestructionAwareBeanPostProcessor的requiresDestruction()判断是否需要销毁
public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) { if (!CollectionUtils.isEmpty(postProcessors)) { for (DestructionAwareBeanPostProcessor processor : postProcessors) { if (processor.requiresDestruction(bean)) { return true; } } } return false; }
以InitDestroyAnnotationBeanPostProcessor.requiresDestruction(bean)为例,查看其实现过程:
在《创建Bean》中介绍@PostConstruct和@PreDestroy的时候,已经详细介绍过findLifecycleMetadata(),通过判断是否有@PreDestroy定义的销毁方法,判断当前Bean是否需要销毁
public boolean requiresDestruction(Object bean) { return findLifecycleMetadata(bean.getClass()).hasDestroyMethods(); } private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) { if (this.lifecycleMetadataCache == null) { // Happens after deserialization, during destruction... return buildLifecycleMetadata(clazz); } LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz); if (metadata == null) { synchronized (this.lifecycleMetadataCache) { metadata = this.lifecycleMetadataCache.get(clazz); if (metadata == null) { metadata = buildLifecycleMetadata(clazz); this.lifecycleMetadataCache.put(clazz, metadata); } return metadata; } } return metadata; }
1.2 注册可以销毁的Bean
判断完Bean是否可以销毁之后,需要注册销毁的Bean,代码如下:
registerDisposableBean()中缓存的是DisposableBeanAdapter对象,即不论该Bean是实现了DisposableBean或AutoCloseable接口,或者是通过BeanDifinition后置处理指定了”(inferred)“销毁方法名或其他方法销毁方法, 还是通过@PreDestroy指定的销毁方法,对于各种销毁逻辑,这里都会将Bean适配成一个DisposableBeanAdapter对象
// Register a DisposableBean implementation that performs all destruction // work for the given bean: DestructionAwareBeanPostProcessors, // DisposableBean interface, custom destroy method. registerDisposableBean(beanName, new DisposableBeanAdapter( bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
public void registerDisposableBean(String beanName, DisposableBean bean) { synchronized (this.disposableBeans) { this.disposableBeans.put(beanName, bean); } }
在DisposableBeanAdapter的构造方法中,会推断出构造方法,并过滤出所有实现了DestructionAwareBeanPostProcessor接口但requiresDestruction()方法返回True的Bean,销毁的时候会调用它们的postProcessBeforeDestruction()
public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition, List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) { …… String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition); if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) && !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) { this.destroyMethodName = destroyMethodName; Method destroyMethod = determineDestroyMethod(destroyMethodName); if (destroyMethod == null) { …… } else { …… destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod); } this.destroyMethod = destroyMethod; } this.beanPostProcessors = filterPostProcessors(postProcessors, bean); }
private static List<DestructionAwareBeanPostProcessor> filterPostProcessors( List<DestructionAwareBeanPostProcessor> processors, Object bean) { List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null; if (!CollectionUtils.isEmpty(processors)) { filteredPostProcessors = new ArrayList<>(processors.size()); for (DestructionAwareBeanPostProcessor processor : processors) { if (processor.requiresDestruction(bean)) { filteredPostProcessors.add(processor); } } } return filteredPostProcessors; }
在Bean销毁的时候,会调用DisposableBeanAdapter的destroy(),这个销毁方法里面会执行各种销毁逻辑
public void destroy() { // 调用所有DestructionAwareBeanPostProcessor.postProcessBeforeDestruction方法 if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { // @PreDestroy定义的销毁方法就是在这一步执行 processor.postProcessBeforeDestruction(this.bean, this.beanName); } } …… }
二、Bean销毁过程
在Spring容器关闭的时候,会去销毁所有的单例Bean,并不是只有注册了销毁逻辑的Bean才被销毁,注册了销毁逻辑的单例Bean在销毁之前,会调用它们注册的销毁逻辑
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); context.close();
容器的close()会调用doClose(),doClose()会调用来销毁单例Bean
public void close() { synchronized (this.startupShutdownMonitor) { doClose(); …… } } protected void doClose() { …… destroyBeans(); …… }
在destroySingletons()中会取出disposableBeans缓存中定了销毁逻辑的Bean的beanName,然后遍历进行销毁
protected void destroyBeans() { getBeanFactory().destroySingletons(); } public void destroySingletons() { …… String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } …… }
在进行销毁的时候,先从单例池等缓存中移除Bean,然后从disposableBeans移除当前DisposableBean并获取该对象,然后调用destroyBean(beanName, disposableBean)执行对象注册的销毁逻辑
public void destroySingleton(String beanName) { // Remove a registered singleton of the given name, if any. // 先从单例池中移除掉 removeSingleton(beanName); // Destroy the corresponding DisposableBean instance. DisposableBean disposableBean; synchronized (this.disposableBeans) { disposableBean = (DisposableBean) this.disposableBeans.remove(beanName); } destroyBean(beanName, disposableBean); } // 从缓存中移除Bean protected void removeSingleton(String beanName) { synchronized (this.singletonObjects) { this.singletonObjects.remove(beanName); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.remove(beanName); } }
在销毁当前Bean的时候,会获取依赖当前Bean的其他Bean的beanName,然后递归调用destroySingleton()方法,保证没有被任何其他Bean依赖的Bean先销毁,在进行销毁时,会先调用DisposableBean的destroy()方法,注册Bean销毁逻辑时已经讲过该方法,然后再去调用其他的销毁逻辑,其他的销毁逻辑无非就是从各种缓存中根据BeanName,清除缓存
protected void destroyBean(String beanName, @Nullable DisposableBean bean) { // dependentBeanMap表示某bean被哪些bean依赖了 // 所以现在要销毁某个bean时,如果这个Bean还被其他Bean依赖了,那么也得销毁其他Bean // Trigger destruction of dependent beans first... Set<String> dependencies; synchronized (this.dependentBeanMap) { // Within full synchronization in order to guarantee a disconnected Set dependencies = this.dependentBeanMap.remove(beanName); } if (dependencies != null) { if (logger.isTraceEnabled()) { logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies); } for (String dependentBeanName : dependencies) { destroySingleton(dependentBeanName); } } // Actually destroy the bean now... if (bean != null) { bean.destroy(); } …… }
-
-
bean的生命周期
2022-05-02 20:38:07六、bean的生命周期 https://liayun.blog.csdn.net/article/details/110670961 Bean 的初始化和销毁; 1、@Bean注解中使用init-method属性和destroy-method属性来指定初始化方法和销毁方法。 初始化和销毁方法的... -
@Bean注解详解
2021-05-14 14:45:19@Bean注解详解 Bean 用法 @Bean表示方法产生一个由Spring管理的bean,属性的名称语义与Spring XML中的 标签配置的一样 public MyBean myBean() { // instantiate and configure MyBean obj return obj; } 当... -
【spring】Bean的Scope与自定义Scope
2022-03-14 16:34:18Bean的作用域 在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为Bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象,除此之外,bean就与应用程序中的其他对象没有什么区别了。... -
Spring bean生命周期详解
2021-09-10 10:49:21Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。 Spring bean生命周期 四个阶段 Bean的实例化阶段 Bean的设置属性阶段 Bean的 初始化阶段 Bean的销毁... -
Spring的Bean加载流程
2021-02-18 02:44:40Spring IOC容器就像是一个生产产品的流水线上的机器,Spring创建出来的Bean就好像是流水线的终点生产出来的一个个精美绝伦的产品。既然是机器,总要先启动,Spring也不例外。因此Bean的加载流程总体上来说可以分为两... -
Spring源码学习笔记——Bean加载
2021-05-19 11:46:09@Bean —> BeanDefinition Bean加载,通过XML解析后得到的Bean 的定义信息( BeanDefinition )获取 Bean 对象实例。 BeanDefinition —> Bean对象 此外,IOC 还具有:自动装配、支持集合、指定初始化方法和销毁... -
Springboot之Bean的加载过程
2020-12-02 22:56:31加载和实例化bean分以下几个重要步骤: 1、根据package扫描出需要被管理的类 2、将这些类封装成BeanDefinition并注册到BeanFactory容器 3、实例化所有扫描到的BeanDefinition,其中包括解决循环依赖、延迟加载问题 ... -
Spring的注解 @Bean用法
2020-12-26 20:08:09随着SpringBoot的流行,基于注解式开发的热潮逐渐覆盖了基于XML纯配置的开发,而作为Spring中最核心的bean当然也能够使用注解的方式进行表示。所以本篇就来详细的讨论一下作为Spring中的Bean到底都有哪些用法。 @... -
较完整的 bean生命周期
2021-03-11 14:47:02首先需要说明的是,Bean的生命周期主要指的是singleton bean,标签的scope默认就是singleton。对prototype bean来说,当用户getBean获得prototype bean的实例后,IOC容器就不再对当前实例进行管理,而是把管理权交由... -
Spring中的普通bean和工厂bean
2021-01-15 21:56:07普通bean和工厂bean普通bean工厂bean(FactoryBean) Spring中有两种类型bean,一种是普通bean,另外一种是工厂bean(FactoryBean)。 区别: 不同点 普通bean 在配置文件中定义bean的类型就是返回类型。 ... -
Spring源码系列-第4章-Bean初始化流程
2022-01-28 10:59:04文章目录第4章-Bean初始化流程流程图-bean初始化流程AbstractApplicationContext#refresh()AbstractApplicationContext#finishBeanFactoryInitialization()DefaultListableBeanFactory#preInstantiateSingletons()... -
SpringBean生命周期详解
2020-10-28 17:51:28SpringBean生命周期详解 一、简述: Spring是我们每天都在使用的框架,Bean是被Spring管理的Java对象,是Spring框架最重要的部分之一,那么让我们一起了解一下Spring中Bean的生命周期是怎样的吧 二、流程图 我们先从... -
Spring中Bean生命周期、实例化与初始化
2021-03-05 09:59:37创建Bean的入口:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean。 实例化 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#... -
面试官:spring中定义bean的方法有哪些?我一口气说出了12种,把面试官整懵了。
2021-07-06 21:17:22我们都知道spring是创建和管理bean的工厂,它提供了多种定义bean的方式,能够满足我们日常工作中的多种业务场景。 那么问题来了,你知道spring中有哪些方式可以定义bean? 我估计很多人会说出以下三种: 最近... -
spring bean创建流程
2020-04-30 17:44:15一、流程图 getBean()流程图,来源:https://blog.csdn.net/laravelshao/article/details/82318063 其它有关图,来源:... ...scope="singleton"单例 在启动(容器初始化之前), 就已经创建了bean,且整个... -
【一篇搞懂】 bean的生命周期详解
2021-06-20 18:33:00Spring中的一个Bean从生到灭要经历很多过程,总体分为Bean定义、实例化、属性赋值(依赖注入)、初始化、生存期、销毁几个阶段: 下面是一个细化的Bean生命周期图: 过程比较复杂,重点关注Bean的... -
Spring 注入 Bean 的七种方式
2021-09-01 00:05:40来源:juejin.cn/post/6844903813753602056通过注解注入Bean背景我们谈到Spring的时候一定会提到IOC容器、DI依赖注入,Spring通过将一个个类标... -
源码分析:Spring是如何获取容器中的Bean?
2020-10-19 23:16:25Spring中Bean注册过程,了解一下 -
Spring源码IOC之创建Bean的前置工作
2021-09-13 00:10:13文章目录2.3 创建bean的实例--前置准备工作2.3.1 getSingleton2.3.1.1 beforeSingletonCreation 前置处理2.3.1.2 afterSingletonCreation 后置处理2.3.1.3 addSingleton 缓存bean实例2.3.2 createBean2.3.1 ... -
Spring框架(2) -- Spring中的Bean
2021-08-07 15:42:28文章目录Spring框架中的Bean1、Bean的配置2、Bean的作用域2.1、singleton作用域2.2、prototype作用域3、Bean的装配方式3.1、基于XML的装配3.1.1、构造器注入3.1.2、setter注入3.1.3、拓展注入3.2、基于Annotation的... -
Spring Bean详细讲解 什么是Bean?
2018-11-19 15:45:37什么是Bean? Spring Bean是被实例的,组装的及被Spring 容器管理的Java对象。 Spring 容器会自动完成@bean对象的实例化。 创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。 Spring ... -
@Bean注解的使用和详解
2020-03-16 14:34:35@Bean:Spring的@Bean注解用于告诉方法,产生一个Bean对象,然后这个Bean对象交给Spring管理。产生这个Bean对象的方法Spring只会调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。 SpringIOC 容器... -
【SpringBoot】Bean 注入失败问题汇总 —— 情人节让Spring教你如何找到对象
2022-05-15 13:56:49最近写项目时遇到了很多关于 Spring 注入 bean 的问题,也是耗费了我很多时间去查找注入失败的原因。因此这篇博客就来总结一下 bean 注入失败的常见情况,希望能对你有所帮助 博客主页:秋刀鱼与猫__ 如果觉得博主... -
@Bean的使用
2019-09-24 14:20:01@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。目的是创建一个类。当spring需要创建指定的一个类时会调用这个注解(@Bean)的方法。 一个对象 @Data @... -
什么是bean
2021-06-20 10:41:25什么是bean? bean是计算机自动生成的类,bean是一个由Spring IoC容器实例化、组装和管理的对象。也就是说,bean并不是程序员编辑的,而是程序运行时,由spring通过反射生成的。在程序中,我们并不使用代码去new一个...