Spring的IOC容器原始碼學習----Bean的定位,載入,解析,註冊,注入
目錄
IOC容器系列包含BeanFactory和ApplicationContext,這兩個介面就是IOC的具體表現形式。
他們的介面關係設計圖如下所示:
主要介面設計主線:
- 1.BeanFactory -> HierarchicalBeanFactory->ConfigurableBeanFacotry
- 2.BeanFactory -> ListableBeanFactory -> ApplicationContext -> WebApplicationContext/ConfigurableApplicationContext
我們常用的應用上下文就是WebApplicationContext/ConfigurableApplicationContext的實現。
BeanFactory
DefaultListableBeanFactory這個基本IOC容器的實現就是實現了ConfigurableBeanFactory,而其他的都是在這個類上做擴充套件。
一般在使用IOC容器的時候,需要如下幾個步驟:
1.建立IOC配置的抽象資源,這個抽象資源包含了BeanDefinition的定義資訊
2.建立一個BeanFactory,比如DefaultListableBeanFactory
3.建立一個載入BeanDefinition的讀取器,例如XmlBeanDefinitionReader來載入xml檔案形式的BeanDefinition,通過一個回撥配置給BeanFactory
4.從定義好的資源位置讀入配置資訊,具體的解析過程就交給XMLBeanDefinitionReader來完成,完整的在和註冊Bean定義之後,需要的Ioc容器就建立好了,這時候就可以使用Ioc容器了
比如我們之前用的XmlBeanFactory,現在已經廢棄使用了。
ClassPathResource classPathResource = new ClassPathResource("beans.xml"); DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); beanDefinitionReader.loadBeanDefinitions(classPathResource);
ApplicationContext
相比使用BeanFactory,大家更偏向使用功能更加強大的ApplicationContext,他在BeanFactory的基礎上添加了很多BeanFactory不具備的功能,
1)支援不同的訊息源。ApplicationContext擴充套件了MessageSource介面,這是資訊源的擴充套件能工可以支援和國際化的實現,為開發多語言版本的應用提供了服務
2) 訪問資源,我們對於ResourceReader和Resource的支援,可以從多個角度獲取資源。
3) 支援應用實踐。繼承了介面ApplicationEventPubliser,從而在上下中引入了時間機制,這些事件和Bean的生命週期的結合為Bean的管理提供了遍歷
4)在ApplicationContext中提供了附加服務,是的ApplicationContext更像面向框架的使用風格。建議開發中使用ApplicationContext作為Ioc容器的基本形式。
例如:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext都是抽象類AbstractXmlApplicationContext的實現,都是從xml配置檔案中獲取資源配置bean,前者是從類路徑獲取配置檔案,後者是通過檔案系統載入。
這兩個類都是都是根據不同的形式讀取resource,都是重寫了抽象父類的getConfigResources模板方法。如下:
// FileSystemXmlApplicationContext這個類中的,其他也都是構造方法,都是不同的入參呼叫下面這個方法 ,Class
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh(); //這是啟動整個IOC 的核心方法
}
}
//這個是讀取資源,封裝為fileSystem資源,這裡重寫了父類的模板方法
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
1.IOC初始化過程
IOC容器的初始化都是如上面的refresh()來啟動的,這個方法標誌著IOC容器的正式啟動,這個啟動包含了BeanDefinitionResource的定位,載入和註冊三個基本過程。這裡把這幾個過程分開做模組就是做到解耦,降低元件的耦合性。
- 1)Resource定位過程,這個指的就是BeanDefinition(bean在IOC中的抽象儲存)資源的定位,它由ResourceLoader通過統一的Resource介面來完成。
- 2) BeanDefinition的載入,就是把使用者定義好的Bean表示為IOC容器內部的資料結構,而這個容器的內部結構就是BeanDefinition。簡單說BeanDefinition就是POJO物件在IOC容器中的抽象,通過這個BeanDefinition定義的資料結構,是Ioc容器能夠方便對POJO物件進行管理。
- 3) 向IOC容器中註冊這些BeanDefinition的過程,這個過程就是通過BeanDefinitionRegistry介面來完成的。這個註冊過程把載入過程解析得到的BeanDefinition向IOC容器進行註冊。內部就是將BeanDefinition注入到一個HashMap中去,IOC容器就是通過這個HashMap來持有這些BeanDefinition資料的。
注意點:IOC容器的初始化不包含Bean的依賴注入。Bean定義的載入和依賴注入是分開的兩個過程。依賴注入一般發生在應用第一次通過getBean向容器索取Bean的時候。不過我們可以通過設定lazyinit屬性,讓容器在初始化的時候就讓這個Bean做到依賴注入,而不用等到第一次getBean的時候呼叫
1.1 BeanDefinition的Resource的定位
Resource介面的實現類圖如下:
例如:
ClassPathResource res = new ClassPathResource("bean.xml");
//表示從當前類路徑去找尋以檔案形式存在的BeanDefinition。
但是獲取到的Resource並不能直接被BeanFactory使用,這裡需要使用BeanDefinitionReader對於這些資訊進行處理,而例如BeanFactory的一種重要實現-DefaultListableBeanFactory只是一個純粹的Ioc容器,需要為他配置特定的讀取器才能完成想用的讀取功能,而ApplicationContext就提供了許多不同的Resource的讀取器來完成讀取。
下面來研究FileSystemXmlApplicationContext
1)結構圖:
通過前面的ConfigurableApplicationContext就繼承了所有ApplicationContext的所有特性,包含ResourceLoader讀入和Resource定義BeanDefinition的能力。
2)這個類原始碼
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
//支援多個配置檔案
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, (ApplicationContext)null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, (ApplicationContext)null);
}
//自己雙親IOC容器
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh(); //呼叫refresh函式載入BeanDefinition。
}
}
//這個方法是在BeanDefinitionReader的loadBeanDefinition方法中被呼叫的,loadBeanDefinition這個方法採用的模板方法,由各個子類來完成的
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
3)重點方法refresh方法分析的呼叫情況
FileSystemXmlApplicationContext中的構造方法呼叫了的refresh方法呼叫了最後呼叫父類的父類的方法refreshBeanFactory,如下面的時序圖。
refreshBeanFactory方法原始碼如下:
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
//這裡通過新建IOC容器。
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
//這裡是模板方法在AbstractBeanDefinitionReader中可以看到具體實現.這裡載入BeanDefinition
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
4)FileSystemXmlApplicationContext中的方法 getResourceByPath方法的呼叫關係圖,上面的refresh方法會呼叫到FileSystemXmlApplicationContext自身的的這個方法:
1.2 BeanDefinition的載入和解析
在完成對BeanDefinition的Resource定位之後就需要了解BeanDefinition的載入過程,這個過程相當於把定義的BeanDefinition在IOC容器中轉化成一個Spring內部表示的資料結果的過程。
1)在AbstractApplicationContext中被子類呼叫的refresh的方法原始碼:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
//啟動子類中的refreshBeanFacotry,包含建立IOC容器
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);//設定BeanFactory後置處理器
this.invokeBeanFactoryPostProcessors(beanFactory); //呼叫BeanFactory的後置處理器,這些後置處理器是在bean定義中想容器註冊的
this.registerBeanPostProcessors(beanFactory);//註冊bean的後置處理器,在Bean建立過程中呼叫
this.initMessageSource(); //初始化上下文中的訊息源
this.initApplicationEventMulticaster();//初始化上下文中的事件機制
this.onRefresh();//這個沒有被實現
this.registerListeners();//檢查監聽器並將這些bean註冊
this.finishBeanFactoryInitialization(beanFactory);//例項化所有的non-lazy-init的bean
this.finishRefresh(); //釋出容器事件,結束refresh過程
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches(); //
}
}
}
2)BeanDefinition解析的時序圖
可以看到在AbstractXmlApplicationContext中的方法,可以採用XmlBeanDefinitionReader來載入BeanDefinition
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
這裡的loadBeanDefinitions方法最終是由XmlBeanDefinitionReader實現。最終處理後的結果由BeanDefinitionHolder來持有結果,除了BeanDefinition外,還包含bean的名字,別名集合,這個BeanDefinitionHolder的生成是通過對Document文件書的內容進行解析來完成的,可以知道這個解析過程是由BeanDefinitionParseDelegate來實現的,具體在processBeanDefinition中實現。
BeanDefinitionParseDelegate包含對Array,List,Set,Map,Prop等各種元素的解析,並生成相應的資料物件。我們可以以AbstractBeanDefintion為入口,讓IOC容器執行索引,查詢和操作。
1.3 BeanDefinition在IOC容器中的註冊
我們對BeanDefinition進行載入和解析後,需要把它向IOC容器中註冊,在DefaultLIstableBeanFactory中有一個map用來儲存這個的
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);
1)我們可以看到時序圖如下:
2)registerBeanDefinition的方法呼叫鏈如下所示:
由於DefaultListableBeanFactory實現了BeanDefinition介面,只需要把這個BeanDefinition設定到hashMap中即可。完成了BeanDefinition的註冊,那麼就完成了IOC容器的初始化偶成,此時在使用IOC容器的DefaultListableBeanFactory中已經建立的整個Bean的配置資訊,而且這些BeanDefinition已經可以被容器使用了。他們都是在beanDefinitionMap裡被檢索和使用。鍵名是beanName;
1.4 IOC容器的依賴注入
依賴注入都是在使用者第一次向容器獲取Bean的時候觸發,但是我們也可以通過控制lazyinit屬性對其預例項化。當我們獲取Bean的時候是通過BeanFactory中的方法getBean方法獲取。其中DefaultListableBeanFactory中的getBean方法都是呼叫基類中的AbstractBeanFactory中的getBean方法。
1)具體原始碼:
//這裡是對BeanFactory介面的實現,比如getBean方法,而這個方法實際上又是呼叫的doGetBean來實現的
public AbstractBeanFactory(BeanFactory parentBeanFactory) {
this.parentBeanFactory = parentBeanFactory;
}
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return this.doGetBean(name, requiredType, (Object[])null, false);
}
public Object getBean(String name, Object... args) throws BeansException {
return this.doGetBean(name, (Class)null, args, false);
}
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return this.doGetBean(name, requiredType, args, false);
}
//這裡是實際取得Bean的地方,也是觸發依賴注入發生的地方
protected <T> T doGetBean(String name, Class<T> requiredType, final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = this.transformedBeanName(name);
//先從快取中獲取單例的bean
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if (sharedInstance != null && args == null) {
if (this.logger.isDebugEnabled()) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
this.logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
this.logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//這裡的完成的是FactoryBean的相關處理,以取得FactoryBean的生產結果
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
} else {
if (this.isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//利用雙親BeanFactory獲取
BeanFactory parentBeanFactory = this.getParentBeanFactory();
if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
String nameToLookup = this.originalBeanName(name);
if (args != null) {
return parentBeanFactory.getBean(nameToLookup, args);
}
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
//通過Bean的名字獲取BeanDefinition
final RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
this.checkMergedBeanDefinition(mbd, beanName, args);
//獲取當前Bean的依賴項,這裡觸發遞迴呼叫,直到沒有依賴為止
String[] dependsOn = mbd.getDependsOn();
String[] var11;
if (dependsOn != null) {
var11 = dependsOn;
int var12 = dependsOn.length;
for(int var13 = 0; var13 < var12; ++var13) {
String dep = var11[var13];
if (this.isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
this.registerDependentBean(dep, beanName);
this.getBean(dep);
}
}
//判斷是單例的情況下,使用createBean方法建立bean
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
} catch (BeansException var2) {
AbstractBeanFactory.this.destroySingleton(beanName);
throw var2;
}
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) { //這裡建立prototype的bean
var11 = null;
Object prototypeInstance;
try {
this.beforePrototypeCreation(beanName);
prototypeInstance = this.createBean(beanName, mbd, args);
} finally {
this.afterPrototypeCreation(beanName);
}
bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
Scope 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, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
AbstractBeanFactory.this.beforePrototypeCreation(beanName);
Object var1;
try {
var1 = AbstractBeanFactory.this.createBean(beanName, mbd, args);
} finally {
AbstractBeanFactory.this.afterPrototypeCreation(beanName);
}
return var1;
}
});
bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException var21) {
throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var21);
}
}
} catch (BeansException var23) {
this.cleanupAfterBeanCreationFailure(beanName);
throw var23;
}
}
//最後對bean的型別檢查,通過後就返回bean,這個bean包含依賴關係的bean
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return this.getTypeConverter().convertIfNecessary(bean, requiredType);
} catch (TypeMismatchException var22) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var22);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
} else {
return bean;
}
}
2)getBean方法呼叫的時序圖
重要方法是createBeanInstance和populateBean方法,這個生成bean的方式很多,可以通過工廠方式,也可以autowire,CCLIB例項化等。SimpleInstantiationStrategy類是用來生成bean物件的預設實現類,提供兩種例項化java物件的方法,一種是通過BeanUtil(JVM反射技術),另外一個種就是CGLIB技術。後面通過BeanDefinitionResolver對BeanDefinition進行解析,然後注入到property中。
這裡也基本都是摘錄《Spring技術內幕》裡面的內容。描述了一個大概的IOC內容,如果錯誤,多多指教。