1. 程式人生 > 其它 >AbstractBeanFactory#getBean(java.lang.String)執行流程分析

AbstractBeanFactory#getBean(java.lang.String)執行流程分析

在執行DefaultListableBeanFactory#preInstantiateSingletons方法時會建立非懶載入的單例Bean,這個建立過程是通過呼叫AbstractBeanFactory#getBean(java.lang.String)建立的;

下面分析AbstractBeanFactory#getBean(java.lang.String)的執行流程;

AbstractBeanFactory#getBean(java.lang.String)

該方法是一個空殼方法,沒有任何的實現邏輯 真正的邏輯呼叫在doGetBean()中,該介面是實現了BeanFactory的getBean(String name)介面;

AbstractBeanFactory#doGetBean

首先通過呼叫transformedBeanName(name)獲取beanName;

呼叫getSingleton(beanName)嘗試從快取中獲取物件;

判斷條件(sharedInstance ! = null && args == null)是否成立,成立則呼叫getObjectForBeanInstance(sharedInstance, name, beanName, null)返回Bean例項;如果sharedInstance 是普通的單例bean,方法會直接返回bean例項;如果sharedInstance 是 FactoryBean 型別的,則需呼叫 getObject 工廠方法獲取真正的bean例項;

條件(sharedInstance ! = null && args == null)不成立,則走下面的邏輯;

呼叫isPrototypeCurrentlyInCreation(beanName)判斷是否存在多例物件(prototype:多例物件,IOC容器啟動的時候,IOC容器啟動並不會去呼叫方法建立物件, 而是每次獲取的時候才會呼叫方法建立物件)的迴圈依賴,存在則丟擲BeanCurrentlyInCreationException異常;

注:Spring解決了單例物件的屬性注入的迴圈依賴,而構造器注入的迴圈依賴沒有解決;

建立兩個迴圈依賴的多例Bean,如下:

@Scope("prototype")
@Component
public class InstanceC {
	private final static Log LOG = LogFactory.getLog(InstanceC.class);

	@Autowired
	private InstanceD instanceD;

//	@Autowired
//	public InstanceC(InstanceD instanceD) {
//		this.instanceD = instanceD;
//	}

	public InstanceC() {
		LOG.info("InstanceC constructor");
	}

	public void invoke() {
		LOG.info("C invoke...");
	}

	public InstanceD getInstanceD() {
		return instanceD;
	}
}

  

@Scope("prototype")
@Component
public class InstanceD {
	private final static Log LOG = LogFactory.getLog(InstanceD.class);

	@Autowired
	private InstanceC instanceC;

//	@Autowired
//	public InstanceD(InstanceC instanceC) {
//		this.instanceC = instanceC;
//	}

	public void invoke() {
		LOG.info("D invoke...");
	}

	public InstanceC getInstanceC() {
		return instanceC;
	}
}

  

異常如下:

getParentBeanFactory()判斷AbstractBeanFacotry工廠是否有父工廠(一般情況下是沒有父工廠因為AbstractBeanFactory直接是抽象類,不存在父工廠),存在則根據父工廠呼叫getBean,一般情況下,只有Spring 和SpringMvc整合的時才會有父子容器的概念;

呼叫getMergedLocalBeanDefinition(beanName)會合並父BeanDefinition和子BeanDefinition,子BeanDefinition會覆蓋父BeanDefintion;

測試如下:

public class ComponentC {
	private String name;
	private String id;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	@Override
	public String toString() {
		return "ComponentC{" +
				"name='" + name + '\'' +
				", id='" + id + '\'' +
				'}';
	}
}
public class ComponentD {
	private String name;
	private String id;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	@Override
	public String toString() {
		return "ComponentC{" +
				"name='" + name + '\'' +
				", id='" + id + '\'' +
				'}';
	}
}

  

bean.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="componentC" class="org.example.component.ComponentC" abstract="true">
		<property name="id" value="test"></property>
		<property name="name" value="parent"></property>
	</bean>

	<bean id="componentD" class="org.example.component.ComponentD" parent="componentC">
		<property name="name" value="son"></property>
<!--		<property name="id" value="componentD"></property>-->
	</bean>
</beans>

  

@Test
public void xmlConfigTest() {
	ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
	System.out.println(context.getBean("componentD"));
}

執行結果如下:

將bean.xml中註釋開啟,執行結果如下:

處理Bean載入依賴順序,如果dependsOn不為空,則呼叫registerDependentBean(dep, beanName)註冊該Bean的依賴項,getBean(dep)優先建立依賴的物件;

測試如下:

public class DependsA {
	private final static Log LOG = LogFactory.getLog(DependsA.class);

	public DependsA() {
		LOG.info("DependsA");
	}
}

  

public class DependsB {
	private final static Log LOG = LogFactory.getLog(DependsB.class);

	public DependsB() {
		LOG.info("DependsB");
	}
}

  

@Configuration
public class DependOnConfig {

	@Bean
	public DependsA dependsA() {
		return new DependsA();
	}

	@Bean
	@DependsOn(value = {"dependsA"})
	public DependsB dependsB() {
		return new DependsB();
	}
}

 

@Test
public void dependsOnTest() {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DependOnConfig.class);
}

  

執行結果如下:

之後根據BeanDefinition的scope型別建立Bean例項;

下面分析scope為single型別的Bean例項建立;

呼叫getSingleton(beanName, new ObjectFactory<Object>(){})獲取單例物件;

DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)

首先從單例快取池中獲取Bean例項,如果Bean例項存在,則將物件返回,否則執行建立物件的邏輯;

呼叫beforeSingletonCreation方法標記當前bean要被建立,該方法在Bean建立前呼叫的;

singletonsCurrentlyInCreation 在這裡會把beanName加入進來,標記該Bean正在建立,當第二次進入時,如果出現singletonsCurrentlyInCreation 新增失敗,這個時候出現了迴圈依賴(構造器注入);

ObjectFactory型別的入參singletonFactory呼叫getObject方法,用於返回一個Bean的例項;

在最後會呼叫afterSingletonCreation將singletonsCurrentlyInCreation標記正在建立的bean從集合中移除,addSingleton將建立的Bean例項加入到快取中

而getSingleton方法的入參是一個函式介面,執行建立的邏輯在createBean方法;

AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

這裡會呼叫resolveBeforeInstantiation方法,在註釋中的意思是給後置處理器返回一個代理物件,但一般情況下在此次是不會返回代理物件的,不論是使用JDK代理還是Cglib代理,前提條件需要有一個Bean例項,而此時的Bean例項並沒有建立,代理物件並不會生成,這裡只是將使用者定義的切面資訊進行快取;

之後執行doCreateBean方法,建立Bean例項的流程在這裡;

AbstractAutowireCapableBeanFactory#doCreateBean

呼叫createBeanInstance方法,使用合適的例項化策略建立例項;

判斷傳入的mbd.getFactoryMethodName是否為空,即有無使用@Bean通過工廠方法注入例項;

mbd的factoryMethodName在BeanDefiniton載入時賦值的,如下:

ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod

判斷Bean定義資訊中的resolvedConstructorOrFactoryMethod是否快取,因為需要根據引數確認到底使用哪個構造器,該過程比較消耗效能,所有采用快取機制;

通過Bean的後置處理器進行獲取合適的構造器物件,根據相應的策略建立物件並返回;

Spring中有三級快取,用於解決屬性賦值的迴圈依賴;

此時創建出來的Bean沒有進行屬性賦值的,屬於早期物件,isSingleton表示是否為單例,allowCircularReferences預設為true,isSingletonCurrentlyInCreation表示當前beanName的Bean正在建立;

符合條件則呼叫addSingletonFactory方法,該方法把早期物件包裝成一個ObjectFactory暴露到三級快取中;

呼叫populateBean進行屬性賦值;

物件的屬性注入是通過後置處理器處理的;

如@Autowired的屬性注入是通過AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues處理的;

虛擬碼如下:

@Component
public class ComponentA {
	private final static Log LOG = LogFactory.getLog(ComponentA.class);

	@Autowired
	private ComponentB componentB;

	public ComponentA() {
		LOG.info("ComponentA constructor");
	}
}

@Component
public class ComponentB {
	private final static Log LOG = LogFactory.getLog(ComponentB.class);

	public ComponentB() {
		LOG.info("ComponentB constructor");
	}
}

上面的虛擬碼ComponentB是ComponentA的欄位值,需要自動注入;

後置處理器最終會根據注入的型別執行下面的注入邏輯,如欄位的注入,執行AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject方法;

AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

這裡會獲取欄位值的例項物件,並把該欄位例項物件set到依賴該物件的Bean上;

最終通過org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate獲取需要依賴的Bean的例項物件,其實該方法是通過BeanFactory#getBean獲取Bean的例項物件;

呼叫initializeBean進行物件初始化;

AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)

invokeAwareMethods會將實現了XXXAware介面進行方法的回撥,invokeInitMethods方法會將實現InitializingBean介面或@PostConstruct註解修飾的方法進行回撥,applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization則會對BeanPostProcessor介面申明的方法進行回撥;

呼叫registerDisposableBeanIfNecessary方法,註冊銷燬Bean的介面DisposableBean,當Bean生命週期結束時會對該介面的destroy方法進行回撥;

AbstractBeanFactory#getBean(java.lang.String)的大致流程圖

單例Bean建立大致流程圖

Bean生命週期大致流程圖