Spring 原始碼閱讀-1-xml載入解析.md
XML載入解析
1、 載入 Bean 定義
1.1 解析XML 處理Document
XmlBeanDefinitionReader#loadBeanDefinitions
/** * 載入xml資源,解析xml所有標籤,將xml對應的bean封裝為BeanDefinition(真實型別為GenericBeanDefinition),並註冊 * 返回載入的BeanDefinition數量 */ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { // EncodedResource 增加了encoding以及charset屬性 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } // 將當前載入的xml記錄一下。正在載入 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } // 獲取配置資源輸入檔案流物件(xml檔案輸入流) InputStream inputStream = encodedResource.getResource().getInputStream(); // org.xml.sax.InputSource // 封裝為 InputSource 為 xml解析做準備 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 真正的處理 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); .... finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { // 載入結束刪除 this.resourcesCurrentlyBeingLoaded.remove(); } }
此方法為載入xml的最上層方法,首先接在xml配置檔案,然後將檔案封裝為InputSource,然後將真正對xml的處理交給doLoadBeanDefinitions方法處理。
XmlBeanDefinitionReader#doLoadBeanDefinitions
/** * 將任務進一步拆分 * 將xml檔案解析封裝為Document * 然後根據Document註冊bean資訊 * @param inputSource * @param resource * @return * @throws BeanDefinitionStoreException */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { // 獲取對 XML 檔案的驗證模式並載入xml檔案為document Document doc = doLoadDocument(inputSource, resource); // 根據返回的 Document 註冊 Bean 資訊 return registerBeanDefinitions(doc, resource); } ... }
此方法進一步拆分任務,DefaultDocumentLoader#loadDocument 將xml流解析成Document物件,XmlBeanDefinitionReader#registerBeanDefinitions根據Document物件進一步解析,註冊bean資訊。
XmlBeanDefinitionReader#doLoadDocument
/** * 解析XML * 允許使用標準 XML 實體將 XML 片段包含到應用程式上下文定義中,例如將大型 XML 檔案拆分為各種模組 */ protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { // getValidationModeForResource 獲取對 XML 檔案的驗證模式 (1、dtd 2、xsd) 如果未設定將載入流自動判斷模式 // getEntityResolver XML 解析器 return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
XmlBeanDefinitionReader#registerBeanDefinitions中通過DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions在給定的根 元素中註冊每個 bean 定義。
protected void doRegisterBeanDefinitions(Element root) {
....
if (this.delegate.isDefaultNamespace(root)) {
// 處理 beans 的 profile屬性 可以配置多套環境
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 前置處理留給子類處理
preProcessXml(root);
// 真正的解析bean定義
parseBeanDefinitions(root, this.delegate);
// 後置處理留給子類處理
postProcessXml(root);
....
}
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 對beans 處理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
// 對 bean處理
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 處理 預設的 http //w ww.springframework org/scherna beans
parseDefaultElement(ele, delegate);
}
else {
// 處理自定義的 比如 tx下的
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
到此,開始根據root(beans)節點迴圈解析子標籤,子標籤又分為預設標籤以及自定義標籤,這兩種處理方式截然不同。一下都是在說預設標籤,自定義標籤單獨說明。
到此已完成的任務:
- 載入xml resource資源,並且封裝為EncodedResource(可以設定charset)。
- 將xml資源輸入流物件封裝為InputSource,供DocumentLoader載入並解析為Document物件(需要指定Xml解析器,以及XMl格式為標準模式的哪一種(xsd|dtd))。
- 通過Document獲取root節點開始解析,在此之前可以通過前置處理器處理(root),在此之後可以通過後置處理器處理(root)。
- 迴圈解析root下的所有子標籤,標籤分為預設標籤以及自定義標籤,處理截然不同。預設標籤,例如 bean、import等,自定義標籤,例如tx等。
1.2 解析預設標籤
DefaultBeanDefinitionDocumentReader#parseDefaultElement
/**
* beans.xsd 預設四種類型 import bean beans alias
* @param ele
* @param delegate
*/
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// import 型別處理
// 是加上就是遞迴呼叫 載入配置檔案定義bean方法
// XMLBeanDefinitionReader.loadBeanDefinitions(resource)
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// alias 處理
// 跟bean中name屬性處理相同
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// bean 處理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// beans 處理
// 巢狀一不簽到沒啥大的區別 迴圈呼叫 beans 解析過程
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
此方法對各個預設標籤進行不同處理,import與beans實則都是遞迴呼叫解析過程,所以主要看alias與bean。
bean標籤解析
DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將bean 下的各種 預設屬性 等 進行解析 並存放到bdholder物件中
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// bdholder 不為空的情況下檢視 是否還有 自定義 屬性 並解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
// 解析完成後 bdHolder 中會有這個bean標籤的全部資訊
try {
// 然後在委託 BeanDefinitionReaderUtils.registerBeanDefinition 進行註冊
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 通知此bean已經註冊完成
// 為了以後擴充套件的目前沒有實現
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
此方法中BeanDefinitionParserDelegate.parseBeanDefinitionElement對預設標籤進行解析,並將解析後的資訊儲存到BeanDefinitionHolder中
BeanDefinitionParserDelegate#parseBeanDefinitionElement
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;
// id 為null 使用 name 的第一個
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
...
}
// 檢查beanName的在bean容器中的唯一性
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 進一步解析bean的其他所有屬性 就是一個bean中的所有屬性和元素解析成javaBean,即 AbstractBeanDefinition
// xml中的bean有什麼 AbstractBeanDefinition 類 就有什麼
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
...
// 不存在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);
}
}
...
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
此方法中對bean標籤的屬性(只是解析id以及name,其他的也是在下面的方法中解析)進行解析,對子標籤的解析交給下層進行處理(BeanDefinitionParserDelegate#parseBeanDefinitionElement),對bean下的自定義標籤的解析單獨處理。
BeanDefinitionParserDelegate#parseBeanDefinitionElement
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* {@code null} if problems occurred during the parsing of the bean definition.
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
// 一個集合(LinkedList ) 將正在解析的beanname記錄
this.parseState.push(new BeanEntry(beanName));
// 解析 class
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 解析 parent
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
...
// 建立用於承載屬性AbstractBeanDefinition型別的GenericBeanDefinition
// BeanDefinition 是配置檔案<bean>元素標籤在容器中的內部表示形式
// <bean>元素標籤擁有
//class scope lazy-init 等配置屬性, BeanDefinition 則提供了相應的 beanClass scope lazyInit
//屬性, BeanDefinition <bean>中的屬性是 一對應的
// Spring 通過 BeanDefinition 將配置檔案中的<bea >配置資訊轉換為容器的內部表示,並將
//這些 BeanDefinition 註冊到 BeanDefinitionReistry中。 Spring 容器的 BeanDefinitionRegistrγ 就像
//Spring 配置資訊的記憶體資料庫,主要是以 map 的形式儲存,後續操作直接從 BeanDefinition Registry 中讀取配置資訊
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬編碼解析預設 bean的各種預設屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 一下全部是子元素的 解析
// 提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析 meta 元資料 子元素
// 取出全部meta子元素, 將key-value 封裝成 BeanMetadataAttribute物件(還包括)儲存到 BeanMetadataAttributeAccessor中
// AbstractBeanDefinition 集成了 BeanMetadataAttributeAccessor
parseMetaElements(ele, bd);
// 解析 lookup-method
// 抽象方法 返回 指定的bean <lookup-method name="getBean" bean="bean"> getBean 為抽象方法
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method
// 與 lookup-method 的區別是 lookup-method自定義 返回的bean ,replaced-method可以將方法的邏輯進行替換
// 都會 封裝成 MethodOverride 存放到 bd的methodOverrides中
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析 建構函式引數
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
// 解析 qualifier 子元素 作用指定beanName 消除歧義
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
...
return null;
}
此方法首先解析bean標籤的class以及parent,然後建立建立預設的AbstractBeanDefinition,然後通過BeanDefinitionParserDelegate#parseBeanDefinitionAttributes解析bean的各種預設屬性,然後在此方法中解析其他的子標籤(例如lookup-method、property、constructor等)。
BeanDefinitionParserDelegate#parseBeanDefinitionAttributes
/**
* 將給定 bean 元素的屬性應用於給定 bean 定義
* Apply the attributes of the given bean element to the given bean * definition.
* @param ele bean declaration element
* @param beanName bean name
* @param containingBean containing bean definition
* @return a bean definition initialized according to the bean element attributes
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析 singleton 使用singleton會 提示錯誤
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
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));
}
// 在嵌入 beanDifinition 情況下且沒有單獨指定 scope 屬性則使用父類預設的屬性
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
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();
}
// 非true字串或空都將是false
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;
}
至此一個bean的預設標籤解析工作就完成了,xml中bean標籤的所有資訊都被封裝到了AbstractBeanDefinition物件中。
bean下的自定義標籤的解析將單獨說明
在DefaultBeanDefinitionDocumentReader#processBeanDefinition中解析完 bean的預設標籤以及自定義標籤後,需要將儲存著bean定義資訊的物件(BeanDefinitionHolder),通過DefaultListableBeanFactory#registerBeanDefinition方法註冊到Spring容器中。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
...
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 註冊前的最後一次校驗,這裡的校驗不同於之前XML檔案校驗,
// 主要是對 AbstractBeanDefinition 中 methodOverrides 校驗
// 校驗 methodOverrides 是否與工廠方法並存或者 methodOverrides 對應的方法根本不存在
((AbstractBeanDefinition) beanDefinition).validate();
}
...
}
// 執行緒安全的map 所以這裡不需要在使用 synchronized
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 通過 beanName 查詢的 bean 已經存在
if (existingDefinition != null) {
// 一系列判斷
// 是否允許Bean定義重寫 檢查是否允許覆蓋
// 如果對應的 BeanName 已經註冊且在配置中配置了 bean 不允許被覆蓋,則丟擲異常
// 當前要儲存的是否和取出來的是一個物件
// 註冊beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// bean是否被標記為已建立
// 需要全複製
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
// 記錄 bean 名稱
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// bean被新的定義覆蓋了 或者是 單例
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置給定 bean 的所有 bean 定義快取,包括從它派生的 bean 快取
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
至此bean的解析,註冊全部完成
import 除去解析特定的幾個屬性外就是遞迴呼叫beans解析過程。
beans 下包含 beans的情況也是遞迴呼叫beans的解析過程。
aliase 主要是解析預設屬性,並記錄別名與beanname的關聯關係。
已完成的任務:
-
root下的不同子標籤,分別進行解析(import bean beans alias)
-
解析bean標籤所有預設屬性(class、id等)
-
解析bean標籤所有子標籤(property等)
-
解析bean下所有自定義標籤
-
將承載xml中bean標籤全部資訊的AbstractBeanDefinition物件註冊到Spring 容器(其實就是map,儲存到裡面)
注意 自定義標籤的解析未說明,單獨說明。
---- **Cover 《Spring 原始碼深度解析》**