1.springbean初始化和实例化?
2.spring源码解析bean初始化与依赖注入四
3.Spring åå§å
4.Spring IoC:getBean 详解
5.Spring源码-09-Bean工厂之getBean方法
springbean初始化和实例化?
spring配置bean实例化有哪些方式
1.实例化bean的源码三种方法:
(1)构造器
!--体验1--
beanid="personService"class="com.persia.PersonServiceBean"
!--index代表方法的参数序号,由0开始,源码基本的源码类型Type可以不声明--
constructor-argindex="0"value="构造注入的name"/
constructor-argindex="1"type="com.persia.IDaoBean"ref="personDao"/
/bean
对应类
publicPersonServiceBean(Stringname,IDaoBeanpersonDao){
this.name=name;
this.personDao=personDao;
}
!--体现2--
beanid="personDao"class="cn.itcast.dao.impl.PersonDaoBean"/
beanid="personServiceBean"class="cn.itcast.service.impl.PersonServiceBean"
lazy-init="true"init-method="init"destroy-method="destory"
!--ref属性对应idpersonDao值name属性对应接口的getter方法名称--
propertyname="personDao"ref="personDao"/
!--体验3--
!--注入属性值--
propertyname="name"value=""/
!--Set的注入--
propertyname="sets"
set
valuesets:第一个值/value
valuesets:第二个值/value
valuesets:第三个值/value
/set
/property
!--List的注入--
propertyname="lists"
list
valuelists:第一个值/value
valuelists:第二个值/value
valuelists:第三个值/value
/list
/property
!--Properties的注入--
propertyname="properties"
props
propkey="props-key1":第一个值/prop
propkey="props-key2":第二个值/prop
propkey="props-key3":第三个值/prop
/props
/property
!--Map的注入--
propertyname="maps"
map
entrykey="maps-key1"value=":第一个值"/
entrykey="maps-key2"value=":第二个值"/
entrykey="maps-key3"value=":第三个值"/
/map
/property
/bean
(2)静态工厂:
!--静态工厂获取bean--
beanid="personService2"class="com.persia.PersonServiceBeanFactory"factory-method="createInstance"/
对应类
publicstaticPersonServiceBeancreateInstance(){
returnnewPersonServiceBean();
}
(3)实例工厂:
没有静态方法,因此配置时,源码先实例化工厂,源码在实例化需要的源码微擎0.8框架源码bean。
!--实例工厂获取bean,源码先实例化工厂再实例化bean--
beanid="fac"class="com.persia.PersonServiceBeanInsFactory"/
beanid="personService3"factory-bean="fac"factory-method="createInstance"/
对应类
publicPersonServiceBeancreateInstance(){
returnnewPersonServiceBean();
}
2.bean的源码作用域
默认情况为单例方式:scope=”singleton”
singleton
单实例作用域,这是源码Spring容器默认的作用域,使用singleton作用域生成的源码是单实例,在整个Bean容器中仅保留一个实例对象供所有调用者共享引用。源码单例模式对于那些无会话状态的源码Bean(如辅助工具类、DAO组件、源码业务逻辑组件等)是源码最理想的选择。
prototype
原型模式,源码这是多实例作用域,针对每次不同的请求,Bean容器均会生成一个全新的Bean实例以供调用者使用。prototype作用域非常适用于那些需要保持会话状态的Bean实例,有一点值得注意的就是,Spring不能对一个prototype
Bean的整个生命周期负责,容器在初始化、装配好一个prototype实例后,将它交给客户端,随后就对该prototype实例不闻不问了。因此,客户端要负责prototype实例的生命周期管理。
request
针对每次HTTP请求,Spring容器会根据Bean的定义创建一个全新的Bean实例,
且该Bean实例仅在当前HTTPrequest内有效,因此可以根据需要放心地更改所建实例的内部状态,
而其他请求中根据Bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。
当处理请求结束,request作用域的Bean实例将被销毁。该作用域仅在基于web的Spring
ApplicationContext情形下有效。
session
针对某个HTTP
Session,Spring容器会根据Bean定义创建一个全新的Bean实例,且该Bean实例仅在当前HTTPSession内有效。
与request作用域一样,我们可以根据需要放心地更改所创建实例的内部状态,而别的HTTPSession中根据Bean定义创建的实例,
将不会看到这些特定于某个HTTPSession的状态变化。当HTTPSession最终被废弃的时候,在该HTTP
Session作用域内的Bean实例也会被废弃掉。该作用域仅在基于Web的SpringApplicationContext情形下有效。
globalsession
global
session作用域类似于标准的HTTP
Session作用域,不过它仅仅在基于portlet的saas商城java源码Web应用中才有意义。portlet规范定义了全局Session的概念,它被所有构成某个portlet
Web应用的各种不同的portlet所共享。在globalsession作用域中定义的Bean被限定于全局portlet
Session的生命周期范围内。如果我们是在编写一个标准的基于Servlet的Web应用,并且定义了一个或多个具有global
session作用域的Bean,系统会使用标准的HTTPSession作用域,并且不会引起任何错误。该作用域仅在基于Web的Spring
ApplicationContext情形下有效。
3.bean的生命周期
(1)什么时候实例化?
对于单例的形式,在容器实例化的时候对bean进行实例化的。
ApplicationContextctx=newClassPathXmlApplicationContext(newString[]{ "applicationContext.xml"});
单实例可以通过lazy-init=”true”,在getBean时进行实例化。
在beans里面default-lazy-init=”true”对所有bean进行延迟处理。
对于prototype,则是在getBean的时候被实例化的。
(2)在bean被实例化之后执行资源操作等方法:
Init-method=””
(3)在bean销毁之前执行的方法:
Destroy-method=””
什么时候被销毁?随着spring容器被关闭时被销毁。
调用spring容器的close方法来正常关闭。以前是随着应用程序执行完而关闭。
在Spring装载配置文件后,Spring工厂实例化完成,开始处理
(1)使用默认构造方法或指定构造参数进行Bean实例化。
(2)根据property标签的配置调用Bean实例中的相关set方法完成属性的赋值。
(3)如果Bean实现了BeanNameAware接口,则调用setBeanName()方法传入当前Bean的ID。
(4)如果Bean实现了BeanFactoryAware接口,则调用setBeanFactory()方法传入当前工厂实例的引用。
(5)如果Bean实现了ApplicationContextAware接口,则调用setApplicationContext()方法传入当前ApplicationContext实例的引用。
(6)如果有BeanPostProcessor与当前Bean关联,则与之关联的对象的postProcess-BeforeInitialzation()方法将被调用。
(7)如果在配置文件中配置Bean时设置了init-method属性,则调用该属性指定的初始化方法。
(8)如果有BeanPostProcessor与当前Bean关联,则与之关联的对象的postProcess-AfterInitialzation()方法将被调用。
(9)Bean实例化完成,处于待用状态,可以被正常使用了。
()当Spring容器关闭时,如果Bean实现了DisposableBean接口,则destroy()方法将被调用。
()如果在配置文件中配置Bean时设置了destroy-method属性,则调用该属性指定的方法进行销毁前的一些处理。
()Bean实例被正常销毁。
Spring系列(一)SpringMVCbean解析、注册、实例化流程源码剖析
最近在使用SpringMVC过程中遇到了一些问题,网上搜索不少帖子后虽然找到了答案和解决方法,龙虎擒牛源码但这些答案大部分都只是给了结论,并没有说明具体原因,感觉总是有点不太满意。
更重要的是这些所谓的结论大多是抄来抄去,基本源自一家,真实性也有待考证。
那作为程序员怎么能知其所以然呢?
此处请大家内心默读三遍。
用过Spring的人都知道其核心就是IOC和AOP,因此要想了解Spring机制就得先从这两点入手,本文主要通过对IOC部分的机制进行介绍。
在开始阅读之前,先准备好以下实验材料。
IDEA是一个优秀的开发工具,如果还在用Eclipse的建议切换到此工具进行。
IDEA有很多的快捷键,在分析过程中建议大家多用Ctrl+Alt+B快捷键,可以快速定位到实现函数。
Springbean的加载主要分为以下6步:
查看源码第一步是找到程序入口,再以入口为突破口,一步步进行源码跟踪。
JavaWeb应用中的入口就是web.xml。
在web.xml找到ContextLoaderListener,此Listener负责初始化SpringIOC。
contextConfigLocation参数设置了bean定义文件地址。
下面是ContextLoaderListener的官方定义:
翻译过来ContextLoaderListener作用就是负责启动和关闭SpringrootWebApplicationContext。
具体WebApplicationContext是什么?开始看源码。
从源码看出此Listener主要有两个函数,一个负责初始化WebApplicationContext,一个负责销毁。
继续看initWebApplicationContext函数。
在上面的代码中主要有两个功能:
进入CreateWebAPPlicationContext函数
进入determineContextClass函数。
进入configureAndReFreshWebApplicaitonContext函数。
WebApplicationContext有很多实现类。但从上面determineContextClass得知此处wac实际上是XmlWebApplicationContext类,因此进入XmlWebApplication类查看其继承的refresh()方法。
沿方法调用栈一层层看下去。
获取beanFactory。
beanFactory初始化。
加载bean。
读取XML配置文件。
XmlBeanDefinitionReader读取XML文件中的bean定义。
继续查看loadBeanDefinitons函数调用栈,进入到XmlBeanDefinitioReader类的loadBeanDefinitions方法。
最终将XML文件解析成Document文档对象。
上一步完成了XML文件的解析工作,接下来将XML中定义的bean注册到webApplicationContext,继续跟踪函数。html源码怎么引用
用BeanDefinitionDocumentReader对象来注册bean。
解析XML文档。
循环解析XML文档中的每个元素。
下面是默认命名空间的解析逻辑。
不明白Spring的命名空间的可以网上查一下,其实类似于package,用来区分变量来源,防止变量重名。
这里我们就不一一跟踪,以解析bean元素为例继续展开。
解析bean元素,最后把每个bean解析为一个包含bean所有信息的BeanDefinitionHolder对象。
接下来将解析到的bean注册到webApplicationContext中。接下继续跟踪registerBeanDefinition函数。
跟踪registerBeanDefinition函数,此函数将bean信息保存到到webApplicationContext的beanDefinitionMap变量中,该变量为map类型,保存Spring容器中所有的bean定义。
Spring实例化bean的时机有两个。
一个是容器启动时候,另一个是真正调用的时候。
相信用过Spring的同学们都知道以上概念,但是为什么呢?
继续从源码角度进行分析,回到之前XmlWebApplication的refresh()方法。
可以看到获得beanFactory后调用了finishBeanFactoryInitialization()方法,继续跟踪此方法。
预先实例化单例类逻辑。
获取bean。
doGetBean中处理的逻辑很多,为了减少干扰,下面只显示了创建bean的函数调用栈。
创建bean。
判断哪种动态代理方式实例化bean。
不管哪种方式最终都是通过反射的形式完成了bean的实例化。
我们继续回到doGetBean函数,分析获取bean的逻辑。
上面方法中首先调用getSingleton(beanName)方法来获取单例bean,如果获取到则直接返回该bean。方法调用栈如下:
getSingleton方法先从singletonObjects属性中获取bean对象,如果不为空则返回该对象,否则返回null。
那singletonObjects保存的是什么?什么时候保存的呢?
回到doGetBean()函数继续分析。如果singletonObjects没有该bean的对象,进入到创建bean的逻辑。处理逻辑如下:
下面是判断容器中有没有注册bean的逻辑,此处beanDefinitionMap相信大家都不陌生,在注册bean的流程里已经说过所有的bean信息都会保存到该变量中。
如果该容器中已经注册过bean,idea折叠源码注释继续往下走。先获取该bean的依赖bean,如果镩子依赖bean,则先递归获取相应的依赖bean。
依赖bean创建完成后,接下来就是创建自身bean实例了。
获取bean实例的处理逻辑有三种,即Singleton、Prototype、其它(request、session、globalsession),下面一一说明。
如果bean是单例模式,执行此逻辑。
获取单例bean,如果已经有该bean的对象直接返回。如果没有则创建单例bean对象,并添加到容器的singletonObjectsMap中,以后直接从singletonObjects直接获取bean。
把新生成的单例bean加入到类型为MAP的singletonObjects属性中,这也就是前面singletonObjects()方法中获取单例bean时从此Map中获取的原因。
Prototype是每次获取该bean时候都新建一个bean,因此逻辑比较简单,直接创建一个bean后返回。
从相应scope获取对象实例。
判断scope,获取实例函数逻辑。
在相应scope中设置实例函数逻辑。
以上就是Springbean从无到有的整个逻辑。
从源码角度分析bean的实例化流程到此基本接近尾声了。
回到开头的问题,ContextLoaderListener中初始化的WebApplicationContext到底是什么呢?
通过源码的分析我们知道WebApplicationContext负责了bean的创建、保存、获取。其实也就是我们平时所说的IOC容器,只不过名字表述不同而已。
本文主要是讲解了XML配置文件中bean的解析、注册、实例化。对于其它命名空间的解析还没有讲到,后续的文章中会一一介绍。
希望通过本文让大家在以后使用Spring的过程中有“一切尽在掌控之中”的感觉,而不仅仅是稀里糊涂的使用。
SpringBean的初始化本文基于上一篇文章进行续写
上一篇文章地址:SpringBean实例化及构造器选择
1.BeanPostProcessor
查看源码发现BeanPostProcessor提供了两个初始化前后的方法,新建一个接口并重写该接口的这两个方法
1.新建一个InstantiationAwareBeanPostProcessorSpring方法并实现InstantiationAwareBeanPostProcessor接口
InstantiationAwareBeanPostProcessor实现了BeanPostProcessor,所以此处使用InstantiationAwareBeanPostProcessorSpring也可以调用上述2个接口方法
2.UserService类实现InitializingBean接口,并重写afterPropertiesSet方法
3.利用客户端进行调用
4.运行结果
spring的bean到底在什么时候实例化spring的bean在被依赖的时候实例化;
分为以下几种Bean:
1.如果指定的是convertrService,beanPostProcessor等实例的时候,则会在ApplicationContext初始化的时候就实例化;
2.如果指定的是自定义的Bean,那么会在第一次访问的时候实例化;
[被依赖的时候实例化,更明确的说是第一次访问]
springioc容器之Bean实例化和依赖注入 spring中的bean对象和java对象是有些许差别的,spring中的bean包含了java对象,并且是基于java对象在spring中做了一些列的加工,所以说spring中的bean比java对象更加丰富。在spring中bean的实例化有2个时机:下面从springioc容器初始化的时候,预实例化的bean为线索来追溯bean的实例化和依赖注入过程,这个过程涵盖了getBean方法。在springioc容器初始化的时候,触发了所有预实例化的bean的加载,这里必须是非抽象、单例和非懒加载的bean才符合条件进行预实例化。具体bean的实例化是在getBean方法中。这里通过getSingleton先从缓存中获取bean实例。从缓存中获取很好理解,分别从spring容器的一级缓存singletonObjects、二级缓存earlySingletonObjects和三级缓存singletonFactories中获取bean实例。在初次获取bean的时候,这里的缓存肯定为空的,但是对于存在循环依赖的bean,这里的一级或二级缓存就不是空的。在有循环依赖的bean中,这里一级缓存会存在不为空的情况,这个时候通过singletonFactory.getObject的时候,返回的可能是一个bean实例,也有可能是一个提前进行aop的代理对象(正常情况下aop是发生在bean初始化的时候完成的),对于有循环依赖并且需要进行aop的bean,在这里会进行提前aop代理对象的生成。当缓存中没有找到bean实例的时候:通过singletonFactory.gspring源码解析bean初始化与依赖注入四
深入解析Spring源码的bean初始化与依赖注入部分,我们将继续从上一篇文章的
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法入手。
随后,方法调用
org.springframework.beans.factory.support.AbstractBeanFactory#registerDisposableBeanIfNecessary进行注册
紧接着,调用
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean获取bean实例。
在这一过程中,我们到达了
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingleton用于销毁单例bean。
然后,再次深入
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean方法进行bean的创建。
紧接着,调用
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation对bean进行前置解析。
之后,再次返回
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean进行bean实例化。
然后,调用
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean再次获取bean实例。
紧接着,进入
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons进行单例bean的预实例化。
最终,完成bean的初始化后触发回调。
返回
org.springframework.context.support.AbstractApplicationContext#refresh执行上下文刷新,完成bean初始化与依赖注入。
至此,本次关于Spring源码中bean初始化与依赖注入的解析告一段落,以上内容仅供学习参考。
Spring åå§å
åºäºspring 4.1.6ç¬¬è¡ super(parent); å®ä¾åç¶ç±»ï¼AbstractXmlApplicationContextãAbstractRefreshableConfigApplicationContextãAbstractRefreshableApplicationContextãAbstractApplicationContext
ç¬¬è¡ refresh();
第4è¡
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
第è¡
finishRefresh();
第7è¡ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
第2è¡ refreshBeanFactory(); 该æ¹æ³ä¸¤ä¸ªåç±»åæå®ç°
AbstractRefreshableApplicationContext å GenericApplicationContextï¼åé¢ FileSystemXmlApplicationContext æ AbstractRefreshableApplicationContext å®ä¾åã
第è¡
DefaultListableBeanFactory beanFactory = createBeanFactory();
第è¡
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
第è¡ï¼loadBeanDefinitions(resources); 为ä»ä¹æ¯ XmlBeanDefinitionReader è¿ä¸ªç±»ï¼
å 为ï¼BeanDefinitionReaderæ¥å£å¥½å¤å®ç°ç±»ï¼XmlBeanDefinitionReaderåªæ¯å ¶ä¸ä¸ä¸ªåç±»ï¼å 为æ¥éª¤4æå®ä¹å¦ã
第5è¡ï¼documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
第è¡ï¼parseBeanDefinitions(root, this.delegate);
第è¡ï¼å¦ææ¯springçåºç¡å½åå ç´ è§£æï¼importãbeanãbeansãaliasï¼è§£æï¼
parseDefaultElement(ele, delegate);
æå±å ç´ è§£æï¼delegate.parseCustomElement(ele); æå±å ç´ æ¶åå°aopç¸å ³ï¼åé¢çå 容讨论ã
ä¸é¢ä»¥è§£æbean为ä¾
以ä¸å®æ读åã解æã注åï¼ç¶åå° 2ãAbstractApplicationContext ç finishBeanFactoryInitialization(beanFactory);
è¡¥å ç¶åï¼
å®ä¾å DefaultListableBeanFactory çæ¶åï¼è°ç¨ preInstantiateSingletons å®æbeanå®ä¾åï¼ä¸æ¯æ½è±¡çbeanï¼æ¯åä¾çbeanï¼éæå è½½ã
refresh() æ¹æ³ä¸è°ç¨ finishBeanFactoryInitialization(beanFactory);
第è¡ï¼getBeanæ¹æ³ï¼å°è¿æ¯ï¼åºè¯¥æ¯å°ãDefaultListableBeanFactoryï¼å 为åé¢æå®ä¾å该类ã
第è¡ï¼Object singletonInstance = getSingleton(beanName); è·ååä¾å¯¹è±¡ï¼åºè¯¥æ¯å°æ¥ï¼æ¥æ²¡ç¨åï¼å¯è½æ¯å é¨ç¨å§ã
getBean éå
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
getBean --> doGetBean --> createBean --> doCreateBean --> createBeanInstance -- > instanceBean
第è¡ï¼return createBean(beanName, mbd, args);
第è¡ï¼Object beanInstance = doCreateBean(beanName, mbd, args); æ§è¡å®ï¼ç¨BeanWrapperå è£ ã
第è¡ï¼instanceWrapper = createBeanInstance(beanName, mbd, args);
æåä¸è¡ï¼instantiateBean(beanName, mbd)
第è¡ï¼beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);æ§è¡å®ï¼ç¨ BeanWrapperå è£ ã
第9è¡ï¼return getInstantiationStrategy().instantiate(mbd, beanName, parent);
第è¡ï¼return BeanUtils.instantiateClass(constructorToUse);
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
设置 mbd.postProcessed = true;
å¦æç¼åä¸æ²¡æåä¾çbeanï¼åéè¦ä»å¤´å¼å§å建åä¾beanï¼è¿é主è¦æ¯éè½½ getSingleton()æ¹æ³å®ç°åä¾beançå è½½ã
å å«ååå¤çæ¹æ³ï¼beforeSingletonCreation(beanName); å afterSingletonCreation(beanName);
Spring IoC:getBean 详解
接着 Spring IoC:finishBeanFactoryInitialization 详解,我们正式开始学习获取 bean 实例方法,该方法是 Spring 最核心的方法。
单击 preInstantiateSingletons 方法里的 getBean(beanName) 代码,进入该方法。
见 doGetBean 方法详解。
doGetBean
1.解析 beanName,主要是解析别名、去掉 FactoryBean 的修饰符 “&”,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块4已解析过。
2.尝试从缓存中获取 beanName 对应的实例,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块7已解析过。
3.1 返回 beanName 对应的实例对象(主要用于 FactoryBean 的特殊处理,普通 bean 会直接返回 sharedInstance 本身),见代码块1详解。
6.如果不是仅仅做类型检测,而是创建 bean 实例,这里要将 beanName 放到 alreadyCreated 缓存,见代码块5详解。
7.根据 beanName 重新获取 MergedBeanDefinition,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块2已解析过。
8.2 检查 dep 是否依赖于 beanName,即检查是否存在循环依赖,见代码块6详解。
8.4 将 dep 和 beanName 的依赖关系注册到缓存中,见代码块7详解。
9.1 scope 为 singleton 的 bean 创建(新建了一个 ObjectFactory,并且重写了 getObject 方法),见代码块8详解。
9.1.1、9.2.2、9.3.4 创建 bean 实例,限于篇幅,在下篇文章单独解析。
9.1.2、9.2.4、9.3.6 返回 beanName 对应的实例对象,见代码块1详解。
9.2.1 scope 为 prototype 时创建实例前的操作、9.2.3 scope 为 prototype 时 创建实例后的操作,相对应的两个方法,见代码块详解。
代码块1:getObjectForBeanInstance
如果对 FactoryBean 不熟悉的,可以回头去看 Spring IoC:finishBeanFactoryInitialization 详解 中对 FactoryBean 的简单介绍。
6.mbd 为空,但是该 bean 的 BeanDefinition 在缓存中存在,则获取该 bean 的 MergedBeanDefinition,在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块2已经解析过。
8.从 FactoryBean 获取对象实例,见代码块2详解。
代码块2:getObjectFromFactoryBean
3.调用 FactoryBean 的 getObject 方法获取对象实例,见代码块3详解。
5.对 bean 实例进行后续处理,执行所有已注册的 BeanPostProcessor 的 postProcessAfterInitialization 方法,见代码块4详解。
代码块3:doGetObjectFromFactoryBean
很简单的方法,就是直接调用 FactoryBean 的 getObject 方法来获取到对象实例。
细心的同学可以发现,该方法是以 do 开头,看过 Spring IoC:源码总览 的同学知道,我在总览里就特别提到以 do 开头的方法是最终进行实际操作的方法,例如本方法就是 FactoryBean 最终实际进行创建 bean 对象实例的方法。
代码块4:postProcessObjectFromFactoryBean
这边走的是 AbstractAutowireCapableBeanFactory 里的方法。通过前面的介绍,我们知道创建的 BeanFactory 为 DefaultListableBeanFactory,而 DefaultListableBeanFactory 继承了 AbstractAutowireCapableBeanFactory,因此这边会走 AbstractAutowireCapableBeanFactory 的重写方法。
在 Spring IoC:registerBeanPostProcessors 详解 中已经学过 BeanPostProcessor,在创建完 bean 实例后,会执行 BeanPostProcessor 的 postProcessAfterInitialization 方法。
代码块5:markBeanAsCreated
2.这边会将 beanName 对应的 MergedBeanDefinition 移除,然后在之后的代码重新获取,主要是为了使用最新的 MergedBeanDefinition 来进行创建操作。
代码块6:isDependent
这边引入了一个缓存 dependentBeanMap:beanName -> 所有依赖 beanName 对应的 bean 的 beanName 集合。内容比较简单,就是检查依赖 beanName 的集合中是否包含 dependentBeanName,隔层依赖也算。例如:A 依赖了 B,B 依赖了 C,则 A 也算依赖了 C。
代码块7:registerDependentBean
这边又引入了一个跟 dependentBeanMap 类似的缓存,dependenciesForBeanMap:beanName -> beanName 对应的 bean 依赖的所有 bean 的 beanName 集合。
这两个缓存很容易搞混,举个简单例子:例如 B 依赖了 A,则 dependentBeanMap 缓存中应该存放一对映射:其中 key 为 A,value 为含有 B 的 Set;而 dependenciesForBeanMap 缓存中也应该存放一对映射:其中 key 为:B,value 为含有 A 的 Set。
代码块8:getSingleton
5.创建单例前的操作,7.创建单例后的操作,这两个方法是对应的,见代码块9详解。
6.执行 singletonFactory 的 getObject 方法获取 bean 实例,该方法会走文章开头 doGetBean 方法的注释 9.1.1。
8.如果是新的单例对象,将 beanName 和对应的单例对象添加到缓存中,见代码块详解。
代码块9:beforeSingletonCreation、afterSingletonCreation
inCreationCheckExclusions 是要在创建检查排除掉的 beanName 集合,正常为空,可以不管。这边主要是引入了 singletonsCurrentlyInCreation 缓存:当前正在创建的 bean 的 beanName 集合。在 beforeSingletonCreation 方法中,通过添加 beanName 到该缓存,可以预防出现构造器循环依赖的情况。
为什么无法解决构造器循环依赖?
我们之前在 Spring IoC:finishBeanFactoryInitialization 详解 中的代码块7提过,getSingleton 方法是解决循环引用的核心代码。解决逻辑的第一句话:“我们先用构造函数创建一个 “不完整” 的 bean 实例”,从这句话可以看出,构造器循环依赖是无法解决的,因为当构造器出现循环依赖,我们连 “不完整” 的 bean 实例都构建不出来。Spring 能解决的循环依赖有:通过 setter 注入的循环依赖、通过属性注入的循环依赖。
代码块:addSingleton
代码块:beforePrototypeCreation、afterPrototypeCreation
该方法和代码块9的两个方法类似。主要是在进行 bean 实例的创建前,将 beanName 添加到 prototypesCurrentlyInCreation 缓存;bean 实例创建后,将 beanName 从 prototypesCurrentlyInCreation 缓存中移除。这边 prototypesCurrentlyInCreation 存放的类型为 Object,在只有一个 beanName 的时候,直接存该 beanName,也就是 String 类型;当有多个 beanName 时,转成 Set 来存放。
总结
本文介绍了获取 bean 实例的大部分内容,包括先从缓存中检查、 FactoryBean 的 bean 创建、实例化自己的依赖(depend-on 属性)、创建 bean 实例的前后一些标记等,在下篇文章中,将解析创建 bean 的内容。
推荐阅读
Spring源码--Bean工厂之getBean方法
Bean实例化与管理是Spring框架的核心功能之一,其中getBean方法作为获取Bean实例的主要手段,具有重要意义。接下来,我们将深入探讨getBean方法及其相关实现,以期更好地理解Spring Bean工厂的工作机制。
一、getBean方法
getBean方法是Spring容器对外提供的一种接口,用于根据指定的Bean名称获取对应Bean实例。该方法会根据配置信息和缓存机制,找到并返回所需的Bean。
二、doGetBean方法
doGetBean方法是getBean方法的内部实现,负责处理Bean的查找、创建和返回工作。其流程分为以下几个关键步骤:
1. getSingleton
若Bean是单例且已存在,则直接返回缓存的实例,无需重新创建。
2. createBean
若非单例或未找到缓存实例,将进入创建Bean的流程。此过程涉及实例化、属性填充和初始化三个主要步骤。
2.1 实例化
通过调用对应的构造函数或使用默认构造函数创建Bean实例。
2.2 三级缓存
在实例化后,新创建的Bean会首先存储于缓存中,随后被添加到Bean作用域的缓存中,以备后续使用。
2.3 属性填充
通过依赖注入或属性设置方法填充Bean的属性值,确保其具有所需的功能。
2.4 初始化
执行Bean的初始化方法,实现任何特定的初始化逻辑,如配置文件加载或数据库连接等。
三、流程图
为了更直观地展示getBean方法的执行流程,以下流程图详细展示了从查找至返回Bean实例的全过程,包括缓存操作、实例化、属性填充和初始化等关键步骤。
四、循环依赖示意图
在处理循环依赖时,Spring容器会采取特定策略以避免无限循环。以下示意图展示了两个单例Bean(A和B)之间循环依赖的处理过程,以及Spring如何通过延迟初始化等机制解决这一问题。
本文通过深入剖析getBean方法及其相关实现,旨在帮助开发者更好地理解Spring Bean工厂的工作机制。通过掌握这些关键概念与流程,可以更高效地利用Spring框架构建可维护且高性能的应用程序。