1. 程式人生 > >spring學習(四)xml檔案的BeanDefinition讀取

spring學習(四)xml檔案的BeanDefinition讀取

上一節中說明了spring在讀取bean時主要的方法refresh中,首先要把配置和註解的bean檔案以beanDefintion的形式快取起來,這個方法是refreshBeanFactory()

各類呼叫的程式圖如下


@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {//判斷beanFactory是否存在,存在則銷燬。
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();//建立一個beanFactory。這裡使用的是預設的DefaultListableBeanFactory
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
			loadBeanDefinitions(beanFactory);//讀取beanDefinition
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}

loadBeanDefinitions(beanFactory)是由子類實現。不同的load有不同的實現類,此處只介紹xml的讀取操作AbstractXmlApplicationContext類

1、生成一個新的XmlBeanDefinitionReader類。用於讀取配置的bean檔案

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

2、其中最主要的方法是loadBeanDefinitions(beanDefinitionReader);

這個方法中主要的實現方法:呼叫beanDefinitionReader.loadBeanDefinitions,這個方法裡又呼叫瞭如下的方法解析出Document

spring使用的是jaxp解析xml檔案,

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		}
....
}

這裡的documentLoader是使用的預設的DefaultDocumentLoader,也可由自定義設定

	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
		if (logger.isDebugEnabled()) {
			logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
		return builder.parse(inputSource);
	}
建立一個DocumentBuilderFactory,並
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {

		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(namespaceAware);

		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);

			if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
				// Enforce namespace aware for XSD...
				factory.setNamespaceAware(true);
				try {
					factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
				}
				catch (IllegalArgumentException ex) {
					ParserConfigurationException pcex = new ParserConfigurationException(
							"Unable to validate using XSD: Your JAXP provider [" + factory +
							"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
							"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
					pcex.initCause(ex);
					throw pcex;
				}
			}
		}

		return factory;
	}

protected DocumentBuilder createDocumentBuilder(
            DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
            throws ParserConfigurationException {

        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);
        }
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);
        }
        return docBuilder;
    }



3、定義一個Document解析器並進行註冊BeanDefintion,

XmlBeanDefinitionReader.doLoadBeanDefinitions裡呼叫的registerBeanDefinitions方法如下

此處BeanDefinitioinDocumentReader使用的是是DefaultBeanDefinitionDocumentReader。不過此處並不是通過new出來一個BeanDefinitionDocumentReader,而是通過BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

4、DefaultBeanDefinitionDocumentReader的registerBeanDefinition方法主要呼叫瞭如下 的doRegisterBeanDefinition方法

通過DefaultBeanDefinitionDocumentReader進行註冊,建立一個BeanDefinitionParserDelegate

protected void doRegisterBeanDefinitions(Element root) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			Assert.state(this.environment != null, "environment property must not be null");
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			if (!this.environment.acceptsProfiles(specifiedProfiles)) {
				return;
			}
		}

		// any nested <beans> elements will cause recursion in this method. In
		// order to propagate and preserve <beans> default-* attributes correctly,
		// keep track of the current (parent) delegate, which may be null. Create
		// the new (child) delegate with a reference to the parent for fallback purposes,
		// then ultimately reset this.delegate back to its original (parent) reference.
		// this behavior emulates a stack of delegates without actually necessitating one.
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createHelper(readerContext, root, parent);

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}

5、其中的parseBeanDefinitions主解析xml的bean的主要解析類,呼叫了BeanDefinitionParserDelegate.parseBeanDefinitionElement解析beanDefinition

BeanDefinitionParserDelegate.parseCustomElement可以根據指定的xml的namesapce自定義xmlbean的解析

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		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) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

DefaultBeanDefinitionDocumentReader.parseDefaultElement方法如下

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

6、解析定義xml檔案的bean.並註冊,解析出來的的beanDefinition封裝到BeanDefinitionHolder中

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// 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);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}

其中呼叫BeanDefinitionParserDelegate.parseBeanDefinitionElement進行xml的bean的解析

public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));

		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			parseConstructorArgElements(ele, bd);
			parsePropertyElements(ele, bd);
			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;
	}

在解析每個bean時會生成一個beanDefinition的子類,預設生成的是GenerateBeanDefinition,BeanDefinitionParserDelegate.createBeanDefinition方法如下

	protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
			throws ClassNotFoundException {

		return BeanDefinitionReaderUtils.createBeanDefinition(
				parentName, className, this.readerContext.getBeanClassLoader());
	}
其中的BeanDefinitionReaderUtils.createBeanDefinition方法如下
public static AbstractBeanDefinition createBeanDefinition(
			String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {

		GenericBeanDefinition bd = new GenericBeanDefinition();
		bd.setParentName(parentName);
		if (className != null) {
			if (classLoader != null) {
				bd.setBeanClass(ClassUtils.forName(className, classLoader));
			}
			else {
				bd.setBeanClassName(className);
			}
		}
		return bd;
	}

7、註冊beanDefinition

DefaultBeanDefinitionDocumentReader.processBeanDefintion在呼叫BeanDefinitionParserDelegate.parseBeanDefinitionElement方法後得到BeanDefinitionHolder

會對BeanDefinitionHolder進行BeanDefinition的註冊

<pre name="code" class="html">BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String aliase : aliases) {
				registry.registerAlias(beanName, aliase);
			}
		}
	}

呼叫之前的beanFactory進行beanDefinition的註冊,此處實現註冊的beanFactory是DefaultListableBeanFactory

相關推薦

spring學習xml檔案BeanDefinition讀取

上一節中說明了spring在讀取bean時主要的方法refresh中,首先要把配置和註解的bean檔案以beanDefintion的形式快取起來,這個方法是refreshBeanFactory() 各類呼叫的程式圖如下 @Override protected fin

spring學習使用註解代替xml配置

用的是IDEA的maven工程,pom.xml檔案導包依賴省略 一、書寫要匯入容器的實體類 import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation

MyBatis學習XML配置文件之SQL映射的XML文件

元素 數據庫 resultmap ash 有一點 oracl 解決 轉換成 插入語 SQL映射文件常用的元素:    1.select   查詢語句是MyBatis最常用的語句之一。   執行簡單查詢的select元素是非常簡單的: <select id=”sele

Spring 學習——XML 配置裡的 Bean 自動裝配

XML 配置裡的 Bean 自動裝配 •Spring IOC 容器可以自動裝配 Bean. 需要做的僅僅是在 <bean> 的 autowire 屬性裡指定自動裝配的模式 •byType(根據型別自動裝配): 若 IOC 容器中有多個與目標 Bean 型別一致的 Bean. 在這

Spring 學習——注入屬性值細節

字面值 •字面值:可用字串表示的值,可以通過 <value> 元素標籤或 value 屬性進行注入。 •基本資料型別及其封裝類、String 等型別都可以採取字面值注入的方式 •若字面值中包含特殊字元,可以使用 <![CDATA[]]> 把字面值包裹起來。 &

Spring學習Jdbc連線池交個spring管理和事務操作

一、連線池的配置交給Spring管理 1,新增jar包 2,spring的jdbc連線池配置 <!-- 配置連線池 --> <bean id="dataSource" class="org.spring

Spring 學習 註解的使用

分四塊 1.如何將使用註解當前類放入到容器? 將當前類放入Spring容器(如果不新增名稱,則以當前類名為預設名稱) @Component("user")//相當於<bean name="user" class="cn.itcast.bean.U

Spring學習-Bean的三種裝配方式

本篇部落格主要講述Spring中Bean的三種主要裝配:1、在XML中進行顯示配置;2、自動裝配方式;3、在Java中進行顯示配置。下面分別來研究上述三種裝配方式。 在Spring IOC容器中注入依賴資源主要有以下兩種基本實現方式:(1)構造器注入:容器例項

Spring Boot學習

自動配置 pat xml配置 XML 入口 spa ges auto classpath @SpringBootApplication 每一個Spring Boot項目都有一個名為*Application的入口類,入口類中有個main方法,在main方法中使用: Sprin

python學習檔案讀寫

三種模式介紹:#r只讀模式,預設的,未指定模式時為只讀 r 開啟檔案不存在的話,會報錯 ; r+ 讀寫模式 #寫模式 w 會將原來檔案中的內容清空 開啟檔案不存在的話,會新建一個檔案 w+ 寫讀模式,雖然能讀,但是因為把檔案內容清空了,讀到的就是空 #追加模式 a+ 追加讀模式

Maven學習筆記--pom檔案

pom.xml檔案 通過maven構建的專案在根目錄下都會有pom.xml這個檔案 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-i

PE檔案格式學習:匯入表

UPDATE: 在文章的末尾更新了一張圖,在網上找的,有助於理解匯入表的結構 1.概述 匯入表是逆向和病毒分析中比較重要的一個表,在分析病毒時幾乎第一時間都要看一下程式的匯入表的內容,判斷程式大概用了哪些功能。 匯入表是資料目錄表中的第2個元素,排在匯出表的

Spring MVC檔案上傳

檔案上傳步驟   1.寫一個檔案上傳的頁面   2.寫一個檔案上傳的控制器 注意:   1.method="post"   2.enctype="multipart/form-data"   3.檔案型別上傳元件 type="file"   4.接收檔案引數需要使用MultipartFile 型別的引數

Spring Boot學習:使用@SpringBootTest註解進行單元測試

一、簡介 專案中經常會遇到需要單元測試的情況,那麼SpringBoot如何實現這種需求,使用@SpringBootTest註解可以執行環境,測試後臺程式碼。 二、環境準備 eclipse + maven + Spring Boot 三、程式碼示例 pom.xml

Spring學習:Web中的Spring

Spring通常用來開發Web應用。 SpringMVC的執行過程: 我們可以從以下的圖來分析SpringMVC的的執行過程。 1、客戶端在傳送請求的時候,會呼叫DispatcherServlet,Dispatch是SpringMVC的入口,Dispatche

Spring Boot IoC學習依賴注入DI

四、依賴注入DI 簡介 Bean之間的依賴稱為依賴注入。 例:人穿不同的鞋子去完成不同的活動。比如,人穿籃球鞋去打籃球,穿跑步鞋去跑步鍛鍊,穿皮鞋去上班等等。所以人和鞋子就是依賴關係。 我們用程式碼來展現依賴,定義兩個介面,一個事人類(Person),一個是鞋子

Spring學習——配置檔案詳解

一、Bean標籤和屬性 bean標籤,是根標籤beans內部必須包含的標籤,它是用於宣告具體的類的物件! 二、bean標籤使用 ①name屬性:name不能重複,name可以指定多個,逗號隔開。可以使用特殊字元,也可以根據name屬性獲取一個物件 ②id屬性:

JavaWeb學習筆記 xml檔案的解析

1.xml解析 就是獲取xml檔案中元素的屬性或資料。 2.xml常用的解析方式 (1)DOM解析(document object model):會將xml檔案中的內容全部讀出,在記憶體中以樹狀形式儲存。整個文件稱為document物件,屬性稱為attribute物件,元素節點稱為ele

Spring 學習——使用外部屬性檔案

•在配置檔案裡配置 Bean 時, 有時需要在 Bean 的配置裡混入系統部署的細節資訊(例如: 檔案路徑, 資料來源配置資訊等). 而這些部署細節實際上需要和 Bean 配置相分離 •Spring 提供了一個 PropertyPlaceholderConfigurer 的 BeanFactor

Maven學習配置檔案pom.xml

Maven pom.xml 1、定義: POM全稱專案物件模型(Project Object Model)的簡稱,它是Maven專案中的檔案,使用XML表示,名稱叫做pom.xml。不過這個檔案中包含了該專案所有相關資訊(專案唯一ID、專案依賴、專案url、專案開發者等一切相關資訊)