Spring 如何從 IoC 容器中獲取對象?
前情回顧
前面幾篇文章主要分析了 Spring IoC 容器如何初始化,以及解析和注冊我們定義的 bean 信息。
其中,「Spring 中的 IoC 容器」對 Spring 中的容器做了一個概述,「Spring IoC 容器初始化」和「Spring IoC 容器初始化(2)」分析了 Spring 如何初始化 IoC 容器,「Spring 是如何解析 <bean> 標簽的?」分析了 Spring 如何解析 <bean> 標簽及其子標簽,并注冊到 BeanFactory。
主要流程如下:

IoC 容器已經(jīng)建立,而且把我們定義的 bean 信息放入了容器,那么如何從容器中獲取對象呢?
本文繼續(xù)分析。
配置及測試代碼
為便于查看,這里再貼一下 bean 配置文件和測試代碼。
- 配置文件 application-ioc.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
???????http://www.springframework.org/schema/beans/spring-beans.xsd">
????<bean?id="person"?class="com.jaxer.spring.ioc.Person">
????????<property?name="pet"?ref="dog"/>
????</bean>
????<bean?id="dog"?class="com.jaxer.spring.ioc.Dog">
????????<property?name="age"?value="1"/>
????????<property?name="owner"?ref="person"/>
????</bean>
</beans>
- 測試代碼
public?class?IocTests?{
????@Test
????public?void?test01()?{
????????ApplicationContext?context?=?new?ClassPathXmlApplicationContext("application-ioc.xml");
????????System.out.println(context.getBean("person"));
????????System.out.println(context.getBean("dog"));
????}
}
/*
?*?輸出結(jié)果:
?*??Person{id=12,?name='Jack-12'}
?*??Dog{age=1}
?*/
如何從容器獲取對象?
從容器中獲取對象是通過 BeanFactory#getBean 方法,它有多個重載的方法,但最終都是通過
AbstractBeanFactory#doGetBean 方法來實現(xiàn)的。doGetBean 方法代碼如下:
public?abstract?class?AbstractBeanFactory?extends?FactoryBeanRegistrySupport?implements?ConfigurableBeanFactory?{
????//?...
????protected?<T>?T?doGetBean(
????????????String?name,?@Nullable?Class<T>?requiredType,?@Nullable?Object[]?args,?boolean?typeCheckOnly)
????????????throws?BeansException?{
????????String?beanName?=?transformedBeanName(name);
????????Object?bean;
????????//?從緩存中獲取單例?bean?對象
????????Object?sharedInstance?=?getSingleton(beanName);
????????if?(sharedInstance?!=?null?&&?args?==?null)?{
????????????//?...
????????????//?處理?FactoryBean?的場景
????????????bean?=?getObjectForBeanInstance(sharedInstance,?name,?beanName,?null);
????????}
????????
????????//?緩存中不存在?bean?對象
????????else?{
????????????if?(isPrototypeCurrentlyInCreation(beanName))?{
????????????????throw?new?BeanCurrentlyInCreationException(beanName);
????????????}
????????????//?bean?對象在父容器中,則從父容器中獲取?bean?對象
????????????BeanFactory?parentBeanFactory?=?getParentBeanFactory();
????????????if?(parentBeanFactory?!=?null?&&?!containsBeanDefinition(beanName))?{
????????????????//?Not?found?->?check?parent.
????????????????String?nameToLookup?=?originalBeanName(name);
????????????????if?(parentBeanFactory?instanceof?AbstractBeanFactory)?{
????????????????????return?((AbstractBeanFactory)?parentBeanFactory).doGetBean(
????????????????????????????nameToLookup,?requiredType,?args,?typeCheckOnly);
????????????????}
????????????????else?if?(args?!=?null)?{
????????????????????//?Delegation?to?parent?with?explicit?args.
????????????????????return?(T)?parentBeanFactory.getBean(nameToLookup,?args);
????????????????}
????????????????else?if?(requiredType?!=?null)?{
????????????????????//?No?args?->?delegate?to?standard?getBean?method.
????????????????????return?parentBeanFactory.getBean(nameToLookup,?requiredType);
????????????????}
????????????????else?{
????????????????????return?(T)?parentBeanFactory.getBean(nameToLookup);
????????????????}
????????????}
????????????//?是否只做類型檢查
????????????if?(!typeCheckOnly)?{
????????????????markBeanAsCreated(beanName);
????????????}
????????????try?{
????????????????//?獲取?BeanDefinition
????????????????RootBeanDefinition?mbd?=?getMergedLocalBeanDefinition(beanName);
????????????????checkMergedBeanDefinition(mbd,?beanName,?args);
????????????????//?獲取依賴的?bean?對象
????????????????//?若創(chuàng)建一個?bean?對象時依賴其他對象,則先創(chuàng)建被依賴對象
????????????????String[]?dependsOn?=?mbd.getDependsOn();
????????????????if?(dependsOn?!=?null)?{
????????????????????for?(String?dep?:?dependsOn)?{
????????????????????????if?(isDependent(beanName,?dep))?{
????????????????????????????//?...
????????????????????????}
????????????????????????registerDependentBean(dep,?beanName);
????????????????????????try?{
????????????????????????????getBean(dep);
????????????????????????}
????????????????????????catch?(NoSuchBeanDefinitionException?ex)?{
????????????????????????????//?...
????????????????????????}
????????????????????}
????????????????}
????????????????//?創(chuàng)建?scope?為?singleton(單例)的對象
????????????????if?(mbd.isSingleton())?{
????????????????????sharedInstance?=?getSingleton(beanName,?()?->?{
????????????????????????try?{
????????????????????????????return?createBean(beanName,?mbd,?args);
????????????????????????}
????????????????????????catch?(BeansException?ex)?{
????????????????????????????//?...
????????????????????????}
????????????????????});
????????????????????//?處理?FactoryBean?的場景
????????????????????bean?=?getObjectForBeanInstance(sharedInstance,?name,?beanName,?mbd);
????????????????}
????????????????//?創(chuàng)建?scope?為?prototype?的對象
????????????????else?if?(mbd.isPrototype())?{
????????????????????//?It's?a?prototype?->?create?a?new?instance.
????????????????????Object?prototypeInstance?=?null;
????????????????????try?{
????????????????????????beforePrototypeCreation(beanName);
????????????????????????prototypeInstance?=?createBean(beanName,?mbd,?args);
????????????????????}
????????????????????finally?{
????????????????????????afterPrototypeCreation(beanName);
????????????????????}
????????????????????//?處理?FactoryBean?的場景
????????????????????bean?=?getObjectForBeanInstance(prototypeInstance,?name,?beanName,?mbd);
????????????????}
????????????????//?創(chuàng)建其他類型對象
????????????????else?{
????????????????????String?scopeName?=?mbd.getScope();
????????????????????if?(!StringUtils.hasLength(scopeName))?{
????????????????????????throw?new?IllegalStateException("No?scope?name?defined?for?bean?′"?+?beanName?+?"'");
????????????????????}
????????????????????Scope?scope?=?this.scopes.get(scopeName);
????????????????????if?(scope?==?null)?{
????????????????????????throw?new?IllegalStateException("No?Scope?registered?for?scope?name?'"?+?scopeName?+?"'");
????????????????????}
????????????????????try?{
????????????????????????Object?scopedInstance?=?scope.get(beanName,?()?->?{
????????????????????????????beforePrototypeCreation(beanName);
????????????????????????????try?{
????????????????????????????????return?createBean(beanName,?mbd,?args);
????????????????????????????}
????????????????????????????finally?{
????????????????????????????????afterPrototypeCreation(beanName);
????????????????????????????}
????????????????????????});
????????????????????????//?處理?FactoryBean?的場景
????????????????????????bean?=?getObjectForBeanInstance(scopedInstance,?name,?beanName,?mbd);
????????????????????}
????????????????????catch?(IllegalStateException?ex)?{
????????????????????????//?...
????????????????????}
????????????????}
????????????}
????????????catch?(BeansException?ex)?{
????????????????cleanupAfterBeanCreationFailure(beanName);
????????????????throw?ex;
????????????}
????????}
????????//?類型檢查
????????if?(requiredType?!=?null?&&?!requiredType.isInstance(bean))?{
????????????try?{
????????????????T?convertedBean?=?getTypeConverter().convertIfNecessary(bean,?requiredType);
????????????????if?(convertedBean?==?null)?{
????????????????????throw?new?BeanNotOfRequiredTypeException(name,?requiredType,?bean.getClass());
????????????????}
????????????????return?convertedBean;
????????????}
????????????catch?(TypeMismatchException?ex)?{
????????????????//?...
????????????}
????????}
????????return?(T)?bean;
????}
}
獲取 bean 對象主要就是通過這個 doGetBean 方法實現(xiàn)的。
該方法雖然看起來稍微有點長,但是呢,它內(nèi)部的實現(xiàn)更長、更復(fù)雜。不過也是有跡可循的,莫慌。
本文先看下這個方法的整體流程,內(nèi)部邏輯后面再慢慢研究。先上流程圖:

代碼雖然有點長,但梳理下來其實也沒那么復(fù)雜了。
這個方法主要做了什么呢?
當(dāng)從容器中獲取 bean 對象時,首先從緩存中獲取。如果緩存中存在,處理 FactoryBean 的場景。
BeanFactory 和 FactoryBean,這哥倆長得很像,也有個別面試題可能會問到。
嗯……以后有機會單獨分析?
如果緩存中沒有,先去父容器獲取,前面創(chuàng)建 BeanFactory 時可以指定 parent 參數(shù),就是那個。
不在父容器中,若 bean 對象依賴了其他對象,則先創(chuàng)建被依賴的 bean 對象,再根據(jù) <bean> 標簽的 scope 屬性去創(chuàng)建相應(yīng)的 bean 對象。
是不是有點像我們平時寫查詢接口時、先從緩存查詢,緩存中沒的話再查詢 DB?
道理是一樣的,空間換時間。
小結(jié)
先整體,后細節(jié)。
本文先從整體上分析了如何從 Spring IoC 容器中獲取 bean 對象,內(nèi)容不多,后文再詳細分解吧。
休息會~
