Spring IOC原始碼詳解之容器初始化
阿新 • • 發佈:2019-02-06
可以進入parseBeanDefinitionElement方法中,裡面詳細講了Bean元素是如何解析的。
//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
//解析Bean定義資原始檔中的<Bean>元素,這個 方 法中主要處理<Bean>元素的id,name
//和別名屬性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//獲取<Bean>元素中的id屬性值
String id = ele.getAttribute(ID_ATTRIBUTE);
//獲取<Bean>元素中的name屬性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
////獲取<Bean>元素中的alias屬性值
List<String> aliases = new ArrayList<String>();
//將<Bean>元素中的所有name屬性值存放到別名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0 );
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean>
//元素中是否包含子<Bean>元素
if (containingBean == null) {
//檢查<Bean>元素所配置的id、name或者別名是否重複
checkNameUniqueness(beanName, aliases, ele);
}
//詳細對<Bean>元素中配置的Bean定義進行解析的地方
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果<Bean>元素中沒有配置id、別名或者name,且沒有包含子//<Bean>元素,為解析的Bean生成一個唯一beanName並註冊
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果<Bean>元素中沒有配置id、別名或者name,且包含了子//<Bean>元素,為解析的Bean使用別名向IoC容器註冊
beanName = this.readerContext.generateBeanName(beanDefinition);
//為解析的Bean使用別名註冊時,為了向後相容 //Spring1.2/2.0,給別名新增類名字尾
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//當解析出錯時,返回null
return null;
}
//詳細對<Bean>元素中配置的Bean定義其他屬性進行解析,由於上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其他屬性資料
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//記錄解析的<Bean>
this.parseState.push(new BeanEntry(beanName));
//這裡只讀取<Bean>元素中配置的class名字,然後載入到BeanDefinition中去
//只是記錄配置的class名字,不做例項化,物件的例項化在依賴注入時完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//如果<Bean>元素中配置了parent屬性,則獲取parent屬性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition
//為載入Bean定義資訊做準備
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//對當前的<Bean>元素中配置的一些屬性進行解析和設定,如配置的單態(singleton)屬性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//為<Bean>元素解析的Bean設定description資訊 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//對<Bean>元素的meta(元資訊)屬性解析
parseMetaElements(ele, bd);
//對<Bean>元素的lookup-method屬性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//對<Bean>元素的replaced-method屬性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<Bean>元素的構造方法設定
parseConstructorArgElements(ele, bd);
//解析<Bean>元素的<property>設定
parsePropertyElements(ele, bd);
//解析<Bean>元素的qualifier屬性
parseQualifierElements(ele, bd);
//為當前解析的Bean設定所需的資源和依賴物件
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
//解析<Bean>元素出錯時,返回null
return null;
}
上面方法中一些對一些配置如元資訊(meta)、qualifier等的解析,我們在Spring中配置時使用的也不多,我們在使用Spring的元素時,配置最多的是property屬性
//解析<property>元素中ref,value或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//如果<property>沒有使用Spring預設的名稱空間,則使用使用者自定義的規則解析//內嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果子元素是bean,則使用解析<Bean>元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果子元素是ref,ref中只能有以下3 個屬性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//獲取<property>元素中的bean屬性值,引用其他解析的Bean的名稱
//可以不再同一個Spring配置檔案中,具體請參考Spring對ref的配置規則
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//獲取<property>元素中的local屬性值,引用同一個Xml檔案中配置
//的Bean的id,local和ref不同,local只能引用同一個配置檔案中的Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//獲取<property>元素中parent屬性值,引用父級容器中的Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
//沒有配置ref的目標屬性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
//建立ref型別資料,指向被引用的物件
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//設定引用型別值是被當前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果子元素是<idref>,使用解析ref元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//如果子元素是<value>,使用解析value元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是null,為<property>設定一個封裝null值的字串資料
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//如果子元素是<array>,使用解析array集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//如果子元素是<list>,使用解析list集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//如果子元素是<set>,使用解析set集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//如果子元素是<map>,使用解析map集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//如果子元素是<props>,使用解析props集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
//既不是ref,又不是value,也不是集合,則子元素配置錯誤,返回null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
上面程式碼中對property元素中配置的Array、List、Set、Map、Prop等各種集合子元素的都通過上述方法解析,生成對應的資料物件,比如ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring物件BeanDefiniton的資料封裝,對集合資料型別的具體解析有各自的解析方法實現。如果想了解其中的一種集合是如何解析的 ,進入相應的方法。
5、Bean是如何在IOC容器中註冊的
下面看解析完的bean是怎樣在IOC容器中註冊的:接著4中的程式碼,進入BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//得到需要 注冊 的 bean名字
String beanName = definitionHolder.getBeanName();
//開始註冊
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 別名也是可以 通過IOC容器和bean聯絡起來的進行註冊
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
上面的registry物件是BeanDefinitionRegistry,上一步是BeanDefinitionReaderUtils.registerBeanDefinition(),即當呼叫BeanDefinitionReaderUtils向IoC容器註冊解析的BeanDefinition時,真正完成註冊功能的是DefaultListableBeanFactory。那麼DefaultListableBeanFactory怎麼會和BeanDefinitionReaderUtils扯到一起呢?
其實上面的類圖還忽略了一點,就是
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}
DefaultListableBeanFactory 實現了BeanDefinitionRegistry介面,這下就豁然開朗,那麼DefaultListableBeanFactory是如何註冊的呢
DefaultListableBeanFactory中使用一個HashMap的集合物件存放IoC容器中註冊解析的BeanDefinition,
@Override
//---------------------------------------------------------------------
// 這裡是IOC容器對BeanDefinitionRegistry介面的實現
//---------------------------------------------------------------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
.....//這裡省略了對BeanDefinition的驗證過程
//先看看在容器裡是不是已經有了同名的bean,如果有丟擲異常。
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
...........
}
else {
//把bean的名字加到IOC容器中去
this.beanDefinitionNames.add(beanName);
}
//這裡把bean的名字和Bean定義聯絡起來放到一個HashMap中去,IOC容器通過這個Map來維護容器裡的Bean定義資訊。
this.beanDefinitionMap.put(beanName, beanDefinition);
removeSingleton(beanName);
}
這樣就完成了Bean定義在IOC容器中的註冊,就可被IOC容器進行管理和使用了
總結IOC初始化過程
-
1、setConfigLocations方法
-
2、初始化的入口在容器實現中的 refresh()呼叫來完成
-
3、AbstractRefreshableApplicationContext實現的 refreshBeanFactory()方法
-
4、建立DefaultListableBeanFactory,並呼叫loadBeanDefinitions(beanFactory)裝載bean定義
-
5、轉到XmlBeanDefinitionReader中的loadBeanDefinitions。
-
6、XmlBeanDefinitionReader通過呼叫其父類DefaultResourceLoader的getResource方法獲取要載入的資源
-
7 、DocumentLoader將Bean定義資源轉換成Document物件
-
8、doLoadBeanDefinitions中進入registerBeanDefinitions 解 析 D ocument物件
-
9、DefaultListableBeanFactory中使用一個HashMap的集合物件存放IoC容器中註冊解析的BeanDefinition,