本次主要想寫spring bean的實例化相關的內容。創建spring bean 實例是spring bean 生命周期的第一階段。bean 的生命周期主要有如下幾個步驟:
- 創建bean的實例
- 給實例化出來的bean填充屬性
- 初始化bea
- 通過IOC容器使用bean
- 容器關閉時銷毀bean
在實例化bean之前在BeanDefinition里頭已經有了所有需要實例化時用到的元數據,接下來spring 只需要選擇合適的實例化方法以及策略即可。實例化方法有兩大類分別是工廠方法和構造方法實例化,后者是最常見的。spring默認的實例化方法就是無參構造函數實例化。
如我們在xml里定義的 <bean id="xxx" class="yyy"/>
以及用注解標識的bean都是通過默認實例化方法實例化的。
- 兩種實例化方法(構造函數 和 工廠方法)
- 源碼閱讀
- 實例化策略(cglib or 反射)
兩種實例化方
使用適當的實例化方法為指定的bean創建新實例:工廠方法,構造函數實例化。
代碼演示
啟動容器時會實例化所有注冊的bean(lazy-init懶加載的bean除外),對於所有單例非懶加載的bean來說當從容器里獲取bean(getBean(String name))的時候不會觸發,實例化階段,而是直接從緩存獲取已准備好的bean,而生成bean的時機則是下面這行代碼運行時觸發的。更多關於懶加載的內容可以參考這篇文章。
Spring lazy-init 原理分析
@Test
public void testBeanInstance(){
// 啟動容器
ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
}
一 使用工廠方法實例化(很少用)
1.靜態工廠方法
public class FactoryInstance {
public FactoryInstance() {
System.out.println("instance by FactoryInstance");
}
}
public class MyBeanFactory {
public static FactoryInstance getInstanceStatic(){
return new FactoryInstance();
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="factoryInstance" class="spring.service.instance.MyBeanFactory"
factory-method="getInstanceStatic"/>
</beans>
輸出結果為:
instance by FactoryInstance
2.實例工廠方法
public class MyBeanFactory {
/**
* 實例工廠創建bean實例
*
* @return
*/
public FactoryInstance getInstance() {
return new FactoryInstance();
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 工廠實例 -- >
<bean id="myBeanFactory" class="MyBeanFactory"/>
<bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>
</beans>
輸出結果為:
instance by FactoryInstance
二 使用構造函數實例化(無參構造函數 & 有參構造函數)
1.無參構造函數實例化(默認的)
public class ConstructorInstance {
public ConstructorInstance() {
System.out.println("ConstructorInstance none args");
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/>
</beans>
輸出結果為:
ConstructorInstance none args
1.有參構造函數實例化
public class ConstructorInstance {
private String name;
public ConstructorInstance(String name) {
System.out.println("ConstructorInstance with args");
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance">
<constructor-arg index="0" name="name" value="test constructor with args"/>
</bean>
</beans>
輸出結果為:
ConstructorInstance with args
源碼閱讀
下面這段是 有關spring bean生命周期的代碼,也是我們本次要討論的bean 實例化的入口。
doCreateBean方法具體實現在doCreateBeanAbstractAutowireCapableBeanFactory類,感興趣的朋友可以進去看看調用鏈。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
//第一步 創建bean實例 還未進行屬性填充和各種特性的初始化
BeanWrapper instanceWrapper = null;
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
Object exposedObject = bean;
try {
// 第二步 進行屬性填充
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
// 第三步 初始化bean 執行初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}catch (Throwable ex) {
// 拋相應的異常
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
我們這里只需關注第一步創建bean實例的流程即可
instanceWrapper = createBeanInstance(beanName, mbd, args);
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 使用工廠方法進行實例化
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Need to determine the constructor...
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 使用帶參構造函數初始化
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 默認實例化方式 無參構造實例化
return instantiateBean(beanName, mbd);
}
上面代碼就是spring 實現bean實例創建的核心代碼。這一步主要根據BeanDefinition里的元數據定義決定使用哪種實例化方法,主要有下面三種:
- instantiateUsingFactoryMethod 工廠方法實例化的具體實現
- autowireConstructor 有參構造函數實例化的具體實現
- instantiateBean 默認實例化具體實現(無參構造函數)
實例化策略(cglib or 反射)
工廠方法的實例化手段沒有選擇策略直接用了發射實現的
實例化策略都是對於構造函數實例化而言的
上面說到的兩構造函數實例化方法不管是哪一種都會選一個實例化策略進行,到底選哪一種策略也是根據BeanDefinition里的定義決定的。
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
上面這一行代碼就是選擇實例化策略的代碼,進入到上面兩種方法的實現之后發現都有這段代碼。
下面選一個instantiateBean的實現來介紹
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
// 在這里選擇一種策略進行實例化
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
選擇使用反射還是cglib
先判斷如果beanDefinition.getMethodOverrides()為空也就是用戶沒有使用replace或者lookup的配置方法,那么直接使用反射的方式,簡單快捷,但是如果使用了這兩個特性,在直接使用反射的方式創建實例就不妥了,因為需要將這兩個配置提供的功能切入進去,所以就必須要使用動態代理的方式將包含兩個特性所對應的邏輯的攔截增強器設置進去,這樣才可以保證在調用方法的時候會被相應的攔截器增強,返回值為包含攔截器的代理實例。---引用自《spring 源碼深度剖析》這本書
<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance" >
<lookup-method name="getName" bean="xxx"/>
<replaced-method name="getName" replacer="yyy"/>
</bean>
如果使用了lookup或者replaced的配置的話會使用cglib,否則直接使用反射。
具體lookup-method和replaced-method的用法可以查閱相關資料。
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
return BeanUtils.instantiateClass(constructorToUse);
}else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
由於篇幅省略了部分代碼