-
前言
- 本篇是IOC容器啓動的最後一遍,也就是註冊。但並不是DI(依賴注入)已經完成,這裏只是完成對xml文件的解析、IOC容器的啓動,具體的依賴注入需要getBean的時候完成。但是也有一個例外:那就是通過控制lazy-init屬性來讓容器實現對bean的預實例化。這個後面我們在講。
- 當代碼讀到這裏,可以再針對前面的代碼有目的的去回顧一下,先走通主線,再根據註釋儘量去理解一些周邊方法。
-
BeanDefinition在IOC容器註冊
首先我們要回憶兩點:1、發起註冊的地方;2、註冊的實現類。
我們先來看第一點,上一篇博客前半部分我們講了Bean的解析,在DefaultBeanDefinitionDocumentReader類的processBeanDefinition方法中,其中一個是去完成BeanDefinition的載入,另一個是完成註冊。我們先來看下簡略代碼:代碼1.1:DefaultBeanDefinitionDocumentReader類的processBeanDefinition方法protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//註冊方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
現在我們找到了BeanDefinition的註冊入口,我們知道BeanDefinitionHolder=BeanDefinition+beanName,所以這裏數據我們是有的,來看下registerBeanDefinition的代碼:
代碼1.2:BeanDefinitionReaderUtils類的registerBeanDefinition方法public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 根據beanName註冊Beandefinition.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果有別名,註冊別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
這裏我們需要回憶第二點了,因爲當我們找這個registerBeanDefinition的實現類的時候,會發現有三個實現類,那麼具體取哪個?還記得我們最初容器建立的時候用的是什麼容器麼?簡單回顧下代碼:
代碼1.3:AbstractRefreshableApplicationContext類的refreshBeanFactory方法@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//創建Factory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
而且,我們看下這個DefaultListFactory實現了BeanDefinitionRegistry接口,這個接口就是定義註冊的。並且這個類中有這麼一個HashMap,這裏就是我們存放BeanDefinition的地方。
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
那麼我們繼續來看代碼:
代碼1.4:DefaultListableBeanFactory類的registerBeanDefinition方法public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
//校驗解析的BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//註冊的過程中需要線程同步,以保證數據的一致性
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//檢查是否有同名的BeanDefinition已經在IoC容器中註冊,如果已經註冊,
//並且不允許覆蓋已註冊的Bean,則拋出註冊失敗異常
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {//如果允許覆蓋,則同名的Bean,後註冊的覆蓋先註冊的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
//IoC容器中沒有已經註冊同名的Bean,按正常註冊流程註冊
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
//重置所有已經註冊過的BeanDefinition的緩存
resetBeanDefinition(beanName);
}
}
完了,這就結束了。到這裏,代碼就完成了IOC容器的初始化過程。此時,在使用的IOC容器DefaultListableBeanFactory中已經建立了整個Bean的配置信息,而且這些BeanDefinition已經可以被容器使用了,他們都在beanDefinitionMap中北檢索和使用。容器的作用就是對這些信息進行處理和維護。這些信息室容器建立依賴反轉的基礎,有了這些基礎數據,就可以完成最後的依賴注入了。