開篇
上一篇講解了 Spring 中的標籤包含自定義標籤和預設標籤,這兩種方式存在較大不同,所以本文主要講解預設標籤的解析過程。
預設標籤的解析是在 parseDefaultElement 方法中。
該方法分別對不同標籤做不同處理。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//對import標籤處理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//對alias標籤處理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//對bean標籤處理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//對beans標籤處理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
doRegisterBeanDefinitions(ele);
}
}
Bean 標籤的解析及註冊
這四種中,我們主要關注對 bean 標籤的解析。bean 標籤的解析是最複雜且重要的。我們進入 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));
}
}
該段程式碼我們還是先看時序圖。
該方法 processBeanDefinition 大致邏輯如下:
- 首先呼叫了
delegate.parseBeanDefinitionElement(ele)
方法進行元素解析。並返回 BeanDefinitionHolder 型別的 bdHolder,經過這個方法後,bdHolder 例項中已經包含了配置檔案中的各種屬性,比如 class,name,id,alias 等 - 當返回的 bdHolder 不為空的情況下,若存在預設標籤的子節點下還有自定義屬性,還要對自定義標籤進行解析。
- 解析完成後,需要對解析後的 bdHolder 進行註冊,註冊操作委託給了
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
方法 - 最後發出響應事件,通知相關監聽器,該 bean 已經載入完成
解析 BeanDefinition
接下來我們一點點分析,首先我們分析該方法delegate.parseBeanDefinitionElement(ele)
。
該方法在BeanDefinitionParserDelegate類中。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
//解析id屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
//解析name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//分割name屬性
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 程式碼(1)
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
//如果不存在beanName那麼根據Spring中提供的命名規則為當前bean生成對應的beanName
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("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);
}
return null;
}
該方法就是對預設標籤解析的全過程,我們現在可以看到對屬性 id、name 的解析。
在當前方法主要完成的內容如下:
- 提取元素 id、name 屬性
- 解析其他屬性並封裝到 GenericBeanDefinition 型別例項中
- 如果檢測到 bean 沒有指定 beanName,則使用預設規則生成一個 beanName
- 將獲取到的資訊封裝到 BeanDefinitionHolder 例項中
我們看一下程式碼中標註的程式碼(1)呼叫的parseBeanDefinitionElement
方法是如何對其他標籤進行解析的。
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//解析class屬性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//解析parent屬性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//程式碼(1)建立用於承載屬性的AbstractBeanDefinition型別的GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//程式碼(2)解析預設bean的各種屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//程式碼(3)解析後設資料
parseMetaElements(ele, bd);
//解析lookup-medthod屬性 (用的很少,這裡就不深入介紹)
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replace-medthod屬性(用的很少,這裡就不深入介紹)
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//程式碼(4)解析建構函式引數
parseConstructorArgElements(ele, bd);
//程式碼(5)解析property子元素
parsePropertyElements(ele, bd);
//解析qualifier子元素
parseQualifierElements(ele, bd);
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();
}
return null;
}
建立用於屬性承載的 BeanDefinition
我們先看一下程式碼(1)呼叫的方法之前,我們先再瞭解一下 BeanDefinition。
BeanDefinition 是一個介面,在 Spring 中存在三種實現:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。它們均繼承自AbstractBeanDefinition,其中 BeanDefinition 是配置檔案<bean>
元素在容器內部的表現形式。該標籤擁有 class、scope、lazy-init 等配置屬性,BeanDefinition 也提供了對應的屬性:beanClass、scope、lazyInit。
其中 RootBeanDefinition 是最常用的實現類,一般對應<bean>
元素標籤,而 GenericBeanDefinition 是 2.5 版本後加入的 bean 檔案配置屬性定義類,提供一站式服務類。
在配置檔案中我們可以父<bean>
和子<bean>
,父就用 RootBeanDefinition 表示,而子就使用 ChildBeanDefinition 表示。普通的<bean>
就使用 RootBeanDefinition 來表示,AbstractBeanDefinition 則對兩者共同類的資訊進行抽象。
Spring 通過 BeanDefinition 將配置檔案的<bean>
轉換為容器內部表示,並且將這些 BeanDefinition 註冊到 BeanDefinitionRegistry 中。
Spring 容器的 BeanDefinitionRegistry 主要以 map 形式儲存,後續操作可以直接從該類中獲取配置資訊。
但首先,我們解析屬性之前就需要建立用於承載屬性的例項,也就是建立了我們之前說的 GenericBeanDefinition 型別的例項。也就是程式碼(1)呼叫的createBeanDefinition(className, parent)
方法。
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
//如果沒有父類,parentName則為空
bd.setParentName(parentName);
if (className != null) {
//如果classLoader不為空則使用傳入的classLoader進行載入類物件,否則只是記錄className
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}else {
bd.setBeanClassName(className);
}
}
return bd;
}
至此,我們就建立好了 GenericBeanDefinition 例項。
解析各種屬性
當建立完用來承載 Bean 資訊的 GenericBeanDefinition 例項後,就可以對 bean 資訊的各種屬性進行解析了。
首先我們進入程式碼(2)parseBeanDefinitionAttributes
方法,該方法對 element 所有元素屬性進行解析。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析singleton屬性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton屬性已經不被支援,使用scope代替
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
//解析scope屬性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
//在嵌入BeanDefinition情況下,並且沒有單獨指定scope屬性,則使用父類預設的屬性
bd.setScope(containingBean.getScope());
}
//解析abstract屬性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//解析lazy-init屬性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//解析autowire屬性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析depends-on屬性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析autowire-candidate屬性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析primary屬性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//解析init-method屬性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析destroy-method屬性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析factory-method屬性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析factory-bean屬性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
該方法主要做的就是拿到各種屬性對應的屬性值放入 AbstractBeanDefinition 對應屬性中。
解析子元素 meta
首先我們回顧一下如何使用 meta 屬性。
<bean id="myTestBean" class="cn.jack.MyTestBean">
<meta key="jack" value="HelloWorld"/>
</bean>
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
這段程式碼並沒有體現在 MyTestBean 中,只是一個宣告,在使用的時候可以使用BeanDefinition類的getAttribute(key)
方法獲取。
接下來我們看一下是如何解析的,進入程式碼(3)。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
//獲取當前節點所有元素
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//判斷節點是否為meta
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
//構造BeanMetadataAttribute例項
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
//記錄資訊
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
解析子元素 constructor-arg
對建構函式的解析還是非常常見的,同時也是很複雜,舉個例子:
<bean id="myTestBean" class="cn.jack.MyTestBean">
<constructor-arg index="0">
<value>Jack</value>
</constructor-arg>
<constructor-arg index="1">
<value>hello</value>
</constructor-arg>
</bean>
該程式碼就是 Spring 中最基礎的配置,自動尋找對應的構造器並在初始化的時候將設定的引數傳入進去,接下來看一下如何解析。
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
//拿到bean所有子節點
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
//解析constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
進入parseConstructorArgElement
方法。
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
//提取index屬性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
//提取type屬性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
//提取name屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
//程式碼(1)解析ele對應的屬性元素
Object value = parsePropertyValue(ele, bd, null);
//使用ConstructorArgumentValues.ValueHolder型別封裝解析出來的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
//將name屬性和type都封裝到valueHolder中
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//不允許重複指定相同引數
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
//新增到BeanDefinition的ConstructorArgumentValues中,存入結構為Map
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
//index為空的處理
try {
this.parseState.push(new ConstructorArgumentEntry());
//解析ele節點對應的屬性值
Object value = parsePropertyValue(ele, bd, null);
//使用ConstructorArgumentValues.ValueHolder型別封裝解析出來的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
//將name屬性和type都封裝到valueHolder中
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
//新增到BeanDefinition的ConstructorArgumentValues中,因為沒有index則存入結構為List
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
該方法並不是特別複雜,首先提取 constructor-arg 上必要的屬性(index、type、name)。
配置中指定了 index 的話操作步驟如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 型別封裝解析後的元素
- 最後將 index、name、type 封裝到 ValueHolder 型別中,並新增到 BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 屬性中。
配置中沒有指定 index 的話操作步驟如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 型別封裝解析後的元素
- 最後將 index、name、type 封裝到 ValueHolder 型別中,並新增到 BeanDefinition 的 constructorArgumentValues 的 genericArgumentValues 屬性中。
瞭解完流程之後,我們看一下具體是如何進行解析的,進入程式碼(1)parsePropertyValue
的方法中。
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 獲取ele節點的子節點,一個屬性只能對應一種型別:ref/value/list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//跳過meta節點或description節點
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
//只能有一個子節點,否則異常
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
//把子節點賦值給subElement
subElement = (Element) node;
}
}
}
//解析constructor-arg的ref屬性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
//解析constructor-arg的value屬性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
//在constructor-arg中不存在: 1.既有ref又有value屬性 2.存在ref或者value屬性並且有子元素
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
// ref屬性處理,使用RuntimeBeanReference封裝對應的ref名稱
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
//value屬性的處理,使用TypedStringValue封裝
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
//解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
//如果沒有ref和value,也沒有子元素則丟擲異常
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
該方法對建構函式中屬性的元素解析,經過以下過程:
- 跳過 description 或者 meta
- 提取 constructor-arg 上的 ref 和 value 屬性,隨後進行校驗
ref 屬性處理,使用 RuntimeBeanReference 封裝對應的 ref 名稱,比如:
<constructor-arg ref="a"></constructor-arg>
value 屬性的處理,使用 TypedStringValue 封裝,比如:
<constructor-arg value="a"></constructor-arg>
子元素處理,比如:
<bean id="myTestBean" class="cn.jack.MyTestBean"> <constructor-arg> <map> <entry key="jack" value="nihao"></entry> </map> </constructor-arg> </bean>
對於子元素的處理,比如這裡提到的加入了 map 元素,是如何處理的?具體在parsePropertySubElement
中實現了各種子元素的處理。
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
//判斷是否為預設名稱空間,如果不是就進行解析自定義節點
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//解析是否為bean節點
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//解析ref標籤
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
//解析parent
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
//解析idref元素
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//解析value元素
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//解析null元素
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//解析array元素
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//解析list元素
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//解析set元素
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//解析map元素
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//解析props元素
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
在該方法中實現了所有支援的型別的分類處理,到此就已經理清楚建構函式是如何解析了,這裡就不深入研究如何解析 list、map 等元素了。
解析子元素 property
在分析完建構函式後,我們可以接著往下看,這裡避免忘記我們再看一下目前到哪裡了。
到這裡我們先回顧一下如何使用 property 屬性。當然,property 屬性裡也可以使用 list 等型別的元素。
<bean id="myTestBean" class="cn.jack.MyTestBean">
<property name="testStr" value="jack"/>
</bean>
public class MyTestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
}
接下來我們看一下是如何解析的。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//獲取到beanElement的所有子節點
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
//解析property節點
parsePropertyElement((Element) node, bd);
}
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//獲取配置元素的name值
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
//name為空則丟擲異常
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//校驗在相同bean節點下,是否存在同樣的name屬性,如果存在則丟擲異常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析屬性值
Object val = parsePropertyValue(ele, bd, propertyName);
//解析後的值和name屬性封裝為PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
//解析meta節點
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
//解析完成後新增到BeanDefinition的propertyValues屬性中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
和之前講解的過程都差不多,都是先獲取所有子標籤然後進行遍歷進行解析,獲取對應的 name、value 值進行封裝。
解析子元素 qualifier
該元素我們一般使用註解偏多,主要就是當介面存在多個實現類時候,在我們注入時指定某一個實現類,這樣 Spring 容器就可以找到對應的 bean。因為在 Spring 中候選的 Bean 數目必須有且僅有一個。解析過程和之前都差不多,這裡就不再贅述。
<bean id="myTestBean" class="cn.jack.MyTestBean">
<qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="Bean的名稱"/>
</bean>
AbstractBeanDefinition 屬性
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
至此,我們就完成了對 XML 文件到 GenericBeanDefinition 的轉換,XML 中的配置都可以在 GenericBeanDefinition 中看到,但 GenericBeanDefinition 只是子類,大部分屬性都在 AbstractBeanDefinition 中。我們回顧一下都有哪些配置。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
// 此處省略靜態變數以及final變數
@Nullable
private volatile Object beanClass;
/**
* bean的作用範圍,對應bean屬性scope
*/
@Nullable
private String scope = SCOPE_DEFAULT;
/**
* 是否是抽象,對應bean屬性abstract
*/
private boolean abstractFlag = false;
/**
* 是否延遲載入,對應bean屬性lazy-init
*/
private boolean lazyInit = false;
/**
* 自動注入模式,對應bean屬性autowire
*/
private int autowireMode = AUTOWIRE_NO;
/**
* 依賴檢查,Spring 3.0後棄用這個屬性
*/
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
/**
* 用來表示一個bean的例項化依靠另一個bean先例項化,對應bean屬性depend-on
*/
@Nullable
private String[] dependsOn;
/**
* autowire-candidate屬性設定為false,這樣容器在查詢自動裝配物件時,
* 將不考慮該bean,即它不會被考慮作為其他bean自動裝配的候選者,
* 但是該bean本身還是可以使用自動裝配來注入其他bean的
*/
private boolean autowireCandidate = true;
/**
* 自動裝配時出現多個bean候選者時,將作為首選者,對應bean屬性primary
*/
private boolean primary = false;
/**
* 用於記錄Qualifier,對應子元素qualifier
*/
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0);
@Nullable
private Supplier<?> instanceSupplier;
/**
* 允許訪問非公開的構造器和方法,程式設定
*/
private boolean nonPublicAccessAllowed = true;
/**
* 是否以一種寬鬆的模式解析建構函式,預設為true,
* 如果為false,則在以下情況
* interface ITest{}
* class ITestImpl implements ITest{};
* class Main {
* Main(ITest i) {}
* Main(ITestImpl i) {}
* }
* 丟擲異常,因為Spring無法準確定位哪個建構函式程式設定
*/
private boolean lenientConstructorResolution = true;
/**
* 對應bean屬性factory-bean,用法:
* <bean id = "instanceFactoryBean" class = "example.chapter3.InstanceFactoryBean" />
* <bean id = "currentTime" factory-bean = "instanceFactoryBean" factory-method = "createTime" />
*/
@Nullable
private String factoryBeanName;
/**
* 對應bean屬性factory-method
*/
@Nullable
private String factoryMethodName;
/**
* 記錄建構函式注入屬性,對應bean屬性constructor-arg
*/
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
/**
* 普通屬性集合
*/
@Nullable
private MutablePropertyValues propertyValues;
/**
* 方法重寫的持有者,記錄lookup-method、replaced-method元素
*/
@Nullable
private MethodOverrides methodOverrides;
/**
* 初始化方法,對應bean屬性init-method
*/
@Nullable
private String initMethodName;
/**
* 銷燬方法,對應bean屬性destroy-method
*/
@Nullable
private String destroyMethodName;
/**
* 是否執行init-method,程式設定
*/
private boolean enforceInitMethod = true;
/**
* 是否執行destroy-method,程式設定
*/
private boolean enforceDestroyMethod = true;
/**
* 是否是使用者定義的而不是應用程式本身定義的,建立AOP時候為true,程式設定
*/
private boolean synthetic = false;
/**
* 定義這個bean的應用,APPLICATION:使用者,INFRASTRUCTURE:完全內部使用,與使用者無關,
* SUPPORT:某些複雜配置的一部分
* 程式設定
*/
private int role = BeanDefinition.ROLE_APPLICATION;
/**
* bean的描述資訊
*/
@Nullable
private String description;
/**
* 這個bean定義的資源
*/
@Nullable
private Resource resource;
}
解析預設標籤中的自定義標籤元素
到目前為止,我們已經分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
這行程式碼,接下來我們繼續分析bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
這行程式碼。
我們先了解一下這行程式碼的大概作用,從語義上來分析:如果需要的話就對 BeanDefinition 進行裝飾,類似於如下場景:
<bean id="myTestBean" class="cn.jack.MyTestBean">
<mybean:user username="jack"/>
</bean>
當 Spring 中的 bean 使用的是預設標籤配置,但是子元素卻使用自定義配置的時候,這行程式碼就會執行。
但是為什麼會在預設型別解析中單獨新增一個自定義型別呢?首先這個自定義型別並不是以 bean 的形式出現的,在這裡的自定義型別其實相當於是屬性。
我們繼續分析該方法程式碼。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
在呼叫decorateBeanDefinitionIfRequired
方法時,第三個引數傳入為 null,該引數是父類 bean,當對某個巢狀配置分析時需要傳遞父類的 BeanDefinition,其實就是為了使用父類的 scope 屬性,如果子類沒有設定 scope 屬性則使用父類的 scope 屬性。這裡是頂層配置,所以傳遞為 null。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
// 遍歷節點,檢視是否存在適用於裝飾的屬性
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
//遍歷子節點,檢視是否存在適用於裝飾的屬性
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}
最終都呼叫到了decorateIfRequired
方法,我們進入此方法檢視。
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
//獲取自定義名稱空間
String namespaceUri = getNamespaceURI(node);
// 過濾預設名稱空間
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
//根據名稱空間找到相應的處理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
//進行裝飾處理
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
}
else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}
到這裡已經很明確了,首先獲取元素或者屬性的名稱空間,然後判斷是否適用於自定義標籤的解析條件,隨後找到對應的 NamespaceHandler 進行下一步解析,該部分會在自定義標籤解析中講解。
總結:該方法的作用就是對自定義標籤或者自定義屬性進行處理,然後找到對應的名稱空間處理器進行進一步的解析。
註冊解析的 BeanDefinition
到這裡,我們對配置檔案的解析、裝飾都已經完成,現在的 BeanDefinition 已經滿足使用要求了,後續就剩下了註冊工作。
也就是 processBeanDefinition 方法中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
這行程式碼。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//獲取beanName做唯一標識註冊
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果有別名的話,註冊所有別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
該方法獲取到 beanName 後,最終 BeanDefinition 都會註冊到BeanDefinitionRegistry中,該方法分為兩部分,一種為 beanName 註冊方式和別名註冊方式。
對於 BeanDefinition 的註冊,不僅僅是將 BeanDefinition 放入 map 中,然後 beanName 作為 key。除此之外還做了別的事情。
進入 DefaultListableBeanFactory 類實現中。
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");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//註冊前最後一次校驗,針對AbstractBeanDefinition中的methodOverrides校驗
//校驗methodOverrides是否與工廠方法並存,或者methodOverrides對應的方法壓根不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//判斷是否已經存在bean
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
//如果對應的beanName已經註冊過並且不允許覆蓋,則丟擲異常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//是否已經開始建立bean
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
// 因為beanDefinitionMap是全域性變數,這裡會存在併發訪問的情況
synchronized (this.beanDefinitionMap) {
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
//存入BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
//記錄beanName
this.beanDefinitionNames.add(beanName);
//從factoryBeanCreatedCache中移除掉這個beanName
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有beanName對應的快取
resetBeanDefinition(beanName);
}
}
註冊 bean 分為以下四步:
- 對 AbstractBeanDefinition 的 methodOverrides 屬性校驗
- 如果已經註冊過並且不允許覆蓋則丟擲異常,否則直接覆蓋
- 加入 map 快取
- 清除解析前的 beanName 快取
之後我們再看通過別名註冊就簡單多了。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
//如果beanName與alias相同則不記錄alias,並刪除對應的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
//不允許覆蓋則丟擲異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
//確保新增的沒有name和alias值相反的資料且alias和name不相等
checkForAliasCircle(name, alias);
//存入map中
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
從該方法可知,註冊 alias 步驟如下:
- alias 和 beanName 相同情況處理,如果相同則不需要處理並刪除原有 alias
- 覆蓋校驗處理
- alias 迴圈檢查
- 註冊 alias
通知監聽器解析及註冊完成
在註冊完成後,當開發人員需要對註冊 BeanDefinition 事件進行監聽時可以通過註冊監聽器方式將處理邏輯寫入監聽器中,在 Spring 中並沒有對此事件做任何邏輯處理。
總結
到這裡,Bean 的解析和註冊過程已經全部 OK 了。
回顧一下,解析 BeanDefinition 的入口在 DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()
。該方法會根據命令空間來判斷標籤是預設標籤還是自定義標籤,其中預設標籤由 parseDefaultElement()
實現,自定義標籤由 parseCustomElement()
實現。在預設標籤解析中,會根據標籤名稱的不同進行 import 、alias 、bean 、beans 四大標籤進行處理,其中 bean 標籤的解析為核心,它由 processBeanDefinition()
方法實現。processBeanDefinition()
開始進入解析核心工作,分為三步:
- 解析預設標籤:
BeanDefinitionParserDelegate.parseBeanDefinitionElement()
- 解析預設標籤下的自定義標籤:
BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired()
- 註冊解析的 BeanDefinition:
BeanDefinitionReaderUtils.registerBeanDefinition()
在預設標籤解析過程中,核心工作由 parseBeanDefinitionElement()
方法實現,該方法會依次解析 Bean 標籤的屬性、各個子元素,解析完成後返回一個 GenericBeanDefinition 例項物件。
最後通過registerBeanDefinition方法進行對BeanDefinition進行註冊後就大功告成了。