【spring源码系列】之【Bean的实例化】
目录
人生需要探索的热情、坚持的勇气以及热爱生活热爱自己的力量。
1. Bean的实例化
上一篇讲述了bean的生命周期,其中第一步就涉及到了bean的实例化,本文重点分析bean实例化,先进入源码中的AbstractAutowireCapableBeanFactory
类中的createBeanInstance
方法。
2. 流程概览
上述图描述了bean的实例化过程中的主要步骤:
- 如果存在
Supplier
回调,则调用obtainFromSupplier()
进行初始化; - 如果存在工厂方法,则使用工厂方法进行初始化;
- 如果构造函数参数不为空,则先获取
autowired
注解的构造函数,再获取构造函数里面的参数,参数是引用类型的话,再次循环调用容器去获取,最后通过反射完成实例化; - 如果构造函数无参,则使用默认无参构造函数实例化;
- 最后将bean添加到一级缓存,并清除二级三级缓存里的bean。
3. 源码分析
3.1 createBeanInstance概览
3.1 使用Supplier
接口
进入obtainFromSupplier方法
上述源码显示从从instanceSupplier
获取,而instanceSupplier
是一个函数式接口:
那在何时对该instanceSupplier
进行设值的呢?不妨来看一下RootBeanDefinition
:
由此可见,在初始化BeanDefinition的时候,就已经将instanceSupplier设值, 随后从instanceSupplier.get()获取,最后包装成BeanWrapper对象后,对其初始化。
3.2 使用FactoryMethod方法实例化
如果工厂方法不为空,则使用工厂方法实例化:
下面进入instantiateUsingFactoryMethod
方法:
先创建一个ConstructorResolver对象,然后调用其instantiateUsingFactoryMethod
方法,该方法细节很多,看如下主要步骤:
这个方法体太多if条件,通过拆解主要流程可以归纳为以下步骤:step1
:获取工厂bean的名字;step2
:判断工厂bean名字是否为空,如果不为空,从容器中获取工厂bean,并将非静态方法设为false;step3
:如果工厂bean的名字为空,则使用静态方法实例化。
上述其他代码都是初始化前工厂方法的遍历,工厂方法的排序、以及参数的获取,最终再调用instantiate方法完成初始化;而instantiate的核心代码就一句话
getInstantiationStrategy()
获取实例化的策略,这里是使用工厂方法来实例化bean,进入instantiate方法:
最终是通过反射完成bean的实例化。
3.3 有参构造函数实例化
如果没有通过工厂方法完成实例化,那么继续玩下走如下代码:
有参构造函数的实例化过程,通过determineConstructorsFromBeanPostProcessors
这个方法完成,同时也是BeanPostProcessor
接口类的应用,最终会调到 AutowiredAnnotationBeanPostProcessor
类的方法,在方法中会扫描有注解的构造函数然后完成装配过程。然后把有@Autowired 注解的构造函数返回。
上面已经拿到了构造函数,autowireConstructor
就是获取参数的过程,其方法比较复杂,跟之前的instantiateUsingFactoryMethod
类似,不再细究,把主要核心代码拿出来分析:
createArgumentArray
方法,获取构造函数参数的值,进入方法,找到核心代码:
resolveAutowiredArgument
方法解析构造函数参数的值,再进入:
再次进入:
最终会感觉眼前一亮,通过非常熟悉的getBean
获取实例,然后会走到普通情况下的getBean方法,通过上面得到一个结论,不管是 Field、Method、还是构造函数中有@Autowired 注解引入的类,都是通过getBean
方法进行实例化获取bean的实例的。
3.4 无参构造函数实例化
无参构造函数的实例化过程 instantiateBean(beanName, mbd)这就是简单的反射实例化。大部分类的实例化都会走这个逻辑。进入实现类的方法:
3.5 bean实例化后的收尾工作
当创建完后,回到上一篇讲的getSingleton
方法:
再来回顾一下该方法:
以及addSingleton相关代码如下:
可以看出getSingleton方法总共做了如下几件事:
- bean创建前,将正在创建的bean放入singletonsCurrentlyInCreation集合;
- bean创建过程中,就是上一篇写的bean的创建整个过程,本篇只是涉及建实例这一个环节;
- bean创建后,从集合singletonsCurrentlyInCreation中移除正在创建的bean;
- bean加入一级缓存,同时移除三级缓存与二级缓存中的bean。
4. 案例
4.1 使用工厂方法创建bean
使用如下配置文件spring.xml,第一种是使用factory-bean属性,bean后面不带class属性;第二种是bean使用class属性,factory-method属性后面是静态工厂方法:
FactoryMethodBean类如下,
实例化类如下:
依赖的属性值分别代表四川、重庆,实现了Province接口:
测试类:
最后结果:
4.2 带有autowired的有参构造函数
测试示例代码:
实例化AutowiredConstructorBean
的代码:
结果会打印如下:
如果再加一个带@Autowired参数的构造函数:
结果就会报错:
通过调试,发现报错的根源是在第二次拿构造函数的参数时,执行了AutowiredAnnotationBeanPostProcessor
中determineCandidateConstructors方法的如下代码:
提示已经有required
修饰的构造函数存在,解决上述问题,如果有两个@Autowired注解的有参构造函数,需要将两个构造函数@Autowired都加上required = false)
。只不过构造函数会根据参数个数的多少降序排序,参数多的构造函数会优先执行,后面的那个构造函数不会被执行。
4.3 不带有autowired的有参构造函数
最后也能完成初始化。
4.4 无参构造函数
通过上面测试案例最后也能完成实例化。
5. 总结
本篇讲述了bean实例化的多种方式,可以学到spring为用户提供多种创建方式,从而看出spring创建bean方式的灵活性,在我们写代码时候,也可以考虑多种策略,来完成某种功能,提高可扩展性与灵活性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)