寫在最前
從十一到現在兩周,IOC部分源碼讀了一半多,是力氣活,也是個精細活。全部寫出來心有餘力不足,是以找些重點寫寫。
簡述 bean 執行個體化之前
Spring中不是啟動容器時就開啟bean的執行個體化程序,它首先會對資源進行讀取并對bean進行初始化。
Spring将資源的定義和資源的加載區分開,
Resource
定義了統一的資源,預設實作是
AbstractResource
,資源的加載由
ResourceLoader
接口的不同實作類傳回Resource。緊接着,解析
Resource
資源,将使用者定義的Bean裝載成
BeanDefinition
,每一個Bean對象都對應着一個
BeanDefinition
。
BeanDefinition
存在于IOC内部容器中的HashMap結構。再緊跟着,向IOC容器注冊解析好的
BeanDefinition
,這個過程是通過BeanDefinitionRegistry接口來實作的。
初始化之後,會進行真正bean的加載,因為
BeanDefinition
不是想要的bean。初始化預設為懶加載,第一次調用
getBean()
時進行初始化。
首先得到可使用正确的beanName,這是涉及到别名或者factoryBean 。bean的作用域有singleton,prototype 和其他,Spring先嘗試從緩存中加載單例bean,否則開始建立bean的執行個體。建立bean的執行個體可以了解為将
BeanDefinition
轉換為
BeanWrapper
,BeanWarpper提供了get、set方法。之後還有一系列處理:MergedBeanDefinitionPostProcessor 屬性合并,單例模式的循環依賴處理,屬性填充,初始化bean。
在初始化bean這個過程中的代碼,就包含了對XXXAware接口的處理,後置處理器,以及自定義的init-method方法。
Bean生命周期
回到最開始,看一下spring bean 的生命周期:
-
執行個體化Bean
對于BeanFactory容器,當客戶向容器請求一個尚未初始化的bean時,或初始化bean的時候需要注入另一個尚未初始化的依賴時,容器就會調用createBean進行執行個體化。
對于ApplicationContext容器,當容器啟動結束後,便執行個體化所有的bean。
容器通過擷取BeanDefination對象中的資訊進行執行個體化。并且這一步僅僅是簡單的執行個體化,并未進行依賴注入。
執行個體化對象被包裝在BeanWrapper對象中,BeanWrapper提供了設定對象屬性的接口,進而避免了使用反射機制設定屬性。
在執行個體化bean的過程中,Spring采用政策模式決定采用反射還是 CGLIB 動态位元組碼。Spring 預設采用 CglibSubclassingInstantiationStrategy執行個體化bean,他既可以以反射執行個體化對象,還可以通過 CGLIB 的動态位元組碼的方式,以方法(setXxx)的方式注入對象執行個體化。
-
設定對象屬性(依賴注入)
執行個體化後的對象被封裝在BeanWrapper對象中,并且此時對象仍然是一個原生的狀态,并沒有進行依賴注入。
緊接着,Spring根據BeanDefinition中的資訊進行依賴注入。
并且通過BeanWrapper提供的設定屬性的接口完成依賴注入。
-
注入Aware接口
緊接着,Spring會檢測該對象是否實作了xxxAware接口,并将相關的xxxAware執行個體注入給bean。
private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
-
BeanPostProcessor
當經過上述幾個步驟後,bean對象已經被正确構造,但如果你想要對象被使用前再進行一次自定義的處理,就可以通過BeanPostProcessor接口實作。
該接口提供了兩個函數:
-
postProcessBeforInitialzation(Object bean, String beanName)
目前正在初始化的bean對象會被傳遞進來,我們就可以對這個bean做任何處理。
這個函數會先于InitialzationBean執行,是以稱為前置處理。
所有Aware接口的注入就是在這一步完成的。
-
postProcessAfterInitialzation(Object bean, String beanName)
目前正在初始化的bean對象會被傳遞進來,我們就可以對這個bean做任何處理。
這個函數會在initialzationBean完成後執行,是以成為後置處理
public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
-
-
InitializingBean 與 init-method
當BeanPostProcessor的前置處理完成後就會進入本階段。
InitialzingBean接口隻有一個函數:
- afterPropertiesSet()
這一階段也可以在bean正式構造完成前增加我們自定義的邏輯,但它與前置處理不同,由于該函數并不會把目前bean傳遞進來,是以這一步沒辦法處理對象本身,隻能增加一些額外的邏輯。若要使用它,我們需要讓bean實作該接口,并把要增加的邏輯寫在該函數中。然後Spring會在前置處理完成後檢測目前bean是否實作了該接口,并執行afterPropertiesSet函數。
當然,Spring為了降低對客戶代碼的侵入性,給bean的配置提供了init-method屬性,該屬性指定了在這一階段需要執行的函數名。Spring便會在初始化階段執行我們設定的函數。init-method本質上仍然使用了InitializingBean的接口。
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
-
DisposableBean 和 destroy-method
和init-method一樣,通過給destroy-method指定函數,就可以在bean銷毀前執行指定的邏輯。
執行個體代碼
先來一段代碼,看看Spring中Bean的生命周期
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, InitializingBean,DisposableBean {
private String test;
public LifeCycleBean() {
System.out.println("構造函數調用...");
}
public String getTest() {
return test;
}
public void display(){
System.out.println("方法調用...");
}
public void setTest(String test) {
System.out.println("屬性注入....");
this.test = test;
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware 被調用...");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware 被調用...");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware 被調用...");
}
public void initMethod(){
System.out.println("init-method 被調用...");
}
public void destroyMethod(){
System.out.println("destroy-method 被調用...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet 被調動...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy 被調動...");
}
}
<bean id="lifeCycleBean" class="com.example.bean.LifeCycleBean"
init-method="initMethod" destroy-method="destroyMethod">
<property name="test" value="test"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
LifeCycleBean lifeCycleBean = (LifeCycleBean) context.getBean("lifeCycleBean");
lifeCycleBean.display();
context.destroy();
}
運作結果如下:
構造函數調用...
屬性注入....
BeanNameAware 被調用...
BeanClassLoaderAware 被調用...
BeanFactoryAware 被調用...
InitializingBean afterPropertiesSet 被調動...
init-method 被調用...
方法調用...
DisposableBean destroy 被調動...
destroy-method 被調用...