Spring 源码学习 - 单例 bean 的实例化过程
本文作者:geek,一个聪明好学的同事
1. 简介
开发中我们常用@Commpont,@Service,@Resource等注解或者配置xml去声明一个类,使其成为spring容器中的bean,以下我将用从源码角度看以AnnotationConfigApplicationContext为例看spring如何把带有注解的类生成spring中bean。
2. 示例代码
注意:以上代码仅需要引入spring-context依赖即可。
3. 源码分析
上面的demo在调用AnnotationConfigApplicationContext构造函数的时候,AppConfig类会被注册到AnnotatedBeanDefinitionReader,由这个reader把AppConfig解释为beanDefination,从而被spring获取到要实例化的类信息,以下为bean生产的源码及其注释。(源码基于springFramework 5.1.X)
3.1 创建入口
单例bean的创建的入口为DefaultListableBeanFactory.java#preInstantiateSingletons,下面源码可见创建的bean条件为非抽象,非@LazyInit注解,Scope为singleTon(默认为singleTon)。
3.2 创建前doGetBean代码逻辑
getBean方法进来后便是直接调用doGetBean,doGetBean执行的源码解释如下:
doGetBean的操作流程如下:
1,执行transformedBeanName方法转换beanName
传递的参数可能是bean的alias或者为FactoryBean,transformedBeanName执行的操作:(1)若传进来的FactoryBean(FactoryBean以&作为前缀标记),去掉&修饰符。(2)经过(1)的处理后,有alias的bean则从aliasMap中获取bean的原始beanName。
2,从容器的缓存中获取bean
getSingleton先从spring三级缓存中的第一级singletonObjects(Map结构)中获取,若不存在,则检查该bean是否正在创建isSingletonCurrentlyInCreation(beanName)) ,正在创建的bean会从二级缓存earlySingletonObjects(Map结构)获取。获取到缓存的bean后会调用AbstractBeanFactory#getObjectForBeanInstance转换bean的实例本身返回。因为从缓存中拿到的可能是factoryBean,所以getObjectForBeanInstance需要把是通过从缓存factoryBeanObjectCache获取或通过factory.getObject()获得相应的bean返回。
3,bean实例化前检查
(1)先检查是否原型模式下的bean是否存在循环依赖,是则会抛异常。
(2)检查父类工厂(parentBeanFactory)是否存在,存在则从parentBeanFactory中递归调用doGetBean。
(3)获取改bean的beanDefinition,检查该bean实例化过程中是否涉及依赖了其他的bean,若是则递归调用getBean,优先创建依赖的bean。(涉及单例下的循环以来解决,下篇文章详细介绍)。
(4)对创建bean代码加synchronized和执行beforeSingletonCreation(beanName)前置处理。
3.3 创建前createBean逻辑
经过前面的doGetBean的一轮检查与准备后,便在AbstractAutowireCapableBeanFactory#createBean中开始bean的创建。
createBean的操作流程如下:
1,resolveBeanClass
解释beanDefinition的class,并且保存在beanDefinition中。
2,prepareMethodOverrides
处理bean中的lookup-method (在单例bean用 @Lookup注解标记的方法,注解的方法返回的对象是原型)和 replace-method( <replaced-method name=A replacer=B/>标记的方法,标记bean中A的方法被实现被另外一个实现MethodReplacer接口的B方法替代)。
3,resolveBeforeInstantiation
调用实现实现BeanPostProcessor的bean后置处理生成代理对象,有代理对象则直接返回代理对象。(spring AOP则是基于此处实现)
3.4 bean的真正实例化createBeanInstance
在AbstractAutowireCapableBeanFactory#createBeanInstance中,真正创建bean,源码及注释如下:
由上述源码可以看出,实例化bean操作流程如下:
1,如果存在 Supplier 回调,则通过提供supplier回调方法创建,如以下方式定义的bean:
2,如果存在工厂方法,则通过工厂方法创建 bean 实例,可以是静态工厂方法或者实例工厂,如以下方式定义的bean:
3,已经有缓存的构造函数或者工厂方法,直接实例化。
4,以上三点都不存在,则使用带参构造函数与无参构造函数实例化。如以下方式定义的bean:
4.总结
spring单例bean的实例化流程大概就是这样,很多细节地方,包括循环依赖处理,bean属性填充等细节点下一章介绍。
参考
查看更多文章关注公众号:好奇心森林
版权声明: 本文为 InfoQ 作者【公众号:好奇心森林】的原创文章。
原文链接:【http://xie.infoq.cn/article/d25fc55668d91fec2c4ed294e】。文章转载请联系作者。
评论