溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行的加載及配置文件的解析

發(fā)布時(shí)間:2021-11-09 17:13:13 來源:億速云 閱讀:116 作者:柒染 欄目:大數(shù)據(jù)

如何進(jìn)行的加載及配置文件的解析,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。

bean的加載-配置文件的解析

spring是個(gè)包含很多個(gè)模塊的框架。其中核心部分有四個(gè),bean,core,context和Expresion Language.

core和bean是spring的基礎(chǔ)模塊。提供Ioc(控制反轉(zhuǎn))和依賴注入特性,這些的基礎(chǔ)概念是BeanFactory。即bean工廠,一個(gè)可以創(chuàng)建各種bean的工廠。我們使用spring來過將項(xiàng)目的時(shí)候,都離不開bean,Bean模塊包含訪問配置文件,創(chuàng)建和管理bean以及進(jìn)行Ioc和DI操作相關(guān)的所有類。

既然bean這么重要,那么就來分析一下bean的加載過程就順理成章了。只分析比較核心的,能夠根據(jù)這些方法想起其中細(xì)節(jié)的一些方法,所以有些地方不會太深入,適合對spring有一定了解的讀者,深究就留給自己去研究吧。

<bean id="xxx" class="com.xx.xx.Person"/>

如上,bean的定義是如此簡單,但是加載過程是怎樣的呢?為了簡便起見,這里還是會使用已經(jīng)過時(shí)的DefaultListableBeanFactory的子類XmlBeanFactory來做分析,源碼使用的是spring 5.1.5.RELEASE。這里是學(xué)習(xí)工程

核心類

DefaultListableBeanFacory

如何進(jìn)行的加載及配置文件的解析

先給一個(gè)類圖,了解一下,有個(gè)印象,后續(xù)會逐步熟悉的。

AliasRegistry: 定義對alias的簡答年增刪改等操作;
SimpleAliasRegistry: 主要是用map作為alias的緩存,并對接口AliasRegistry進(jìn)行實(shí)現(xiàn);
SingletonBeanRegistry: 定義對單例的注冊及獲取;
BeanFactory: 定義獲取bean及bean的各種屬性;
DefaultSingletonBeanRegistry: 對接口SingletonBeanRegistry各函數(shù)的實(shí)現(xiàn);
hierarchicalBeanFactory: 繼承BeanFactory,也就是在beanFactory定義的功能的基礎(chǔ)上增加了對ParrentFactory的支持;
BeanDefintionRegistry: 定義對BeanDefinition的各種增刪改操作;
FactoryBeanRegistrySupport: 在DefaultSingletonBeanRegistry基礎(chǔ)上增加了對FactoryBean的特殊處理功能;
ConfigurableBeanFactory: 提供配置Factory的各種方法;
ListableBeanFactory: 根據(jù)各種條件獲取備案的配置清單;
AbstractBeanFactory: 綜合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能。
AutowireCapableBeanFacotry: 提供創(chuàng)建bean、自動(dòng)注入、初始化以及應(yīng)用bean的后置處理器;
AbstractAutowireCapableBeanFactory: 綜合AbstractBeanFactory并對接口AutowireCapableBeanFactory進(jìn)行實(shí)現(xiàn);
ConfigurableListableBeanFactory: BeanFactory配置清單,指定胡烈類型及接口等;
DefaultListableBeanFactory: 綜合上面內(nèi)所有功能,主要是對Bean注冊后的處理;

XmlBeanFactory是DefaultListableBeanFactory的子類,標(biāo)記為已經(jīng)過時(shí),里面增加了XmlBeanDefinitionReader類型的reader屬性。雖然已經(jīng)標(biāo)記為過時(shí),但是即使是DefaultListableBeanFactory,最終在spring初始化的過程中,也是使用的XmlBeanDefinitionReader來讀取的配置文件。因此不影響我們對代碼的分析。

XmlBeanDefinitionReader

spring的配置文件是實(shí)現(xiàn)bean配置的最常用方式(這里不討論springboot),而讀取xml配置文件的就是XmlBeanDefinitionReader。先來看一下會使用到的一些類的功能。

ResourceLoader: 定義資源加載器,主要應(yīng)用于根據(jù)給定的資源文件地址返回對應(yīng)你的Resource;
BeanDefinitionReader: 主要定義資源文件讀取并轉(zhuǎn)換為BeanDefinition的各個(gè)功能;
EnvironmentCapable: 定義獲取Environment方法;
DocumentLoader: 定義從資源文件加載到轉(zhuǎn)換為Document的功能;
AbstractBeanDefinitionReader: 對EnvironmentCapable、BeanDefinitionReader類定義的功能進(jìn)行實(shí)現(xiàn);
BeanDefinitionDocumentReader: 定義讀取Document并注冊BeanDefinition功能;
BeanDefinitionParserDelegate: 定義解析Element的各種方法;

XmlBeanDefinitionReader通過繼承AbstractBeanDefinitionReader中的方法,來使用ResourseLoader將資源文件路徑轉(zhuǎn)換為對應(yīng)的Resource文件;通過DocumentLoader對Resource文件進(jìn)行轉(zhuǎn)換,將Resource文件轉(zhuǎn)換為Document文件;通過實(shí)現(xiàn)接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader類對Document進(jìn)行解析,并使用BeanDefinitionParserDelegate對Element進(jìn)行解析。

xml文件讀取

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
	super(parentBeanFactory);
    // 開始加載并讀取xml文件
	this.reader.loadBeanDefinitions(resource);
}

構(gòu)造方法中super調(diào)用了父類,一直跟到底,會在AbstractAutowireCapableBeanFactory中發(fā)現(xiàn)以下代碼:

public AbstractAutowireCapableBeanFactory() {
	super();
    // 忽略指定接口的自動(dòng)裝配功能
	ignoreDependencyInterface(BeanNameAware.class);
	ignoreDependencyInterface(BeanFactoryAware.class);
	ignoreDependencyInterface(BeanClassLoaderAware.class);
}

回到構(gòu)造函數(shù)的this.reader.loadBeanDefinitions(resource);

// XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	//...
	// 檢查當(dāng)前線程正正在加載的resource
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (currentResources == null) {
		currentResources = new HashSet<>(4);
		this.resourcesCurrentlyBeingLoaded.set(currentResources);
	}
    //如果當(dāng)前準(zhǔn)備解析的encodeResource正在加載,則拋出異常
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	try {
		//獲取resource的inputStream
		InputStream inputStream = encodedResource.getResource().getInputStream();
		try {
			//包裝成inputSource
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			//開始加載
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		finally {
			inputStream.close();
		}
	}
	//...
}

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
        //將resource轉(zhuǎn)成document,這里頭會去驗(yàn)證xml文件,DTD或者XSD,不同的類型會用不用的解析方式,喜歡的自己去看吧
		Document doc = doLoadDocument(inputSource, resource);
        //解析document并注冊bean
		int count = registerBeanDefinitions(doc, resource);
		//...
        //返回加載的bean的數(shù)量
		return count;
	}
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //創(chuàng)建document解析器
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	int countBefore = getRegistry().getBeanDefinitionCount();
    //使用document解析器解析document
    //這里createReaderContext()創(chuàng)建的是XmlReaderContext
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));//***①
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

public XmlReaderContext createReaderContext(Resource resource) {
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
	if (this.namespaceHandlerResolver == null) {
		this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
	}
	return this.namespaceHandlerResolver;
}

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
	ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
    //返回默認(rèn)的命名空間解析器,這個(gè)東西會用來獲取解析標(biāo)簽的NamespaceHandler
	return new DefaultNamespaceHandlerResolver(cl);
}

看方法***①:

//DefaultBeanDefinitionDocumentReader.java
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
    //解析document
	doRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    //創(chuàng)建默認(rèn)的BeanDefinitionParserDelegate,用來解析自定義標(biāo)簽
	this.delegate = createDelegate(getReaderContext(), root, parent);
    //...profile的處理,如果在web.xml中定義了profile,則只解析相應(yīng)的文件
    if (this.delegate.isDefaultNamespace(root)) {
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// We cannot use Profiles.of(...) since profile expressions are not supported
			// in XML config. See SPR-12458 for details.
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
    //留給子類擴(kuò)展
    preProcessXml(root);
    //真正開始解析
	parseBeanDefinitions(root, this.delegate);
    //留給子類擴(kuò)展
	postProcessXml(root);
}

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)) {
                    //解析默認(rèn)標(biāo)簽
					parseDefaultElement(ele, delegate);
				}
				else {
                    //解析自定義標(biāo)簽
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
        //解析自定義標(biāo)簽
		delegate.parseCustomElement(root);
	}
}

解析默認(rèn)標(biāo)簽

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        //解析import標(biāo)簽,如果有import標(biāo)簽,其實(shí)就會遞歸解析了
		importBeanDefinitionResource(ele);
	}
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        //解析別名標(biāo)簽
		processAliasRegistration(ele);
	}
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        //解析bean標(biāo)簽
		processBeanDefinition(ele, delegate);
	}
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// 解析beans標(biāo)簽,其實(shí)就是把當(dāng)前節(jié)點(diǎn)繼續(xù)丟去解析而已,產(chǎn)生了遞歸
		doRegisterBeanDefinitions(ele);
	}
}

對默認(rèn)標(biāo)簽的解析就不多寫了,無非不過就是怎么去讀取文件,怎么解決文件中的各種數(shù)據(jù),喜歡的就自己深究下去吧,再細(xì)下去就太多了,為了給自己一個(gè)印象,我這里不會深究下去。

解析自定義標(biāo)簽

先看個(gè)demo吧。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myname="http://www.wt.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	   http://www.wt.com/schema/user http://www.wt.com/schema/user.xsd">

    <myname:user id="user" name="wt" age="23" email="taow"/>
</beans>

指定命名空間myname="http://www.wt.com.schema/user",看下面META-INF/Spring.handlers配置定義了當(dāng)前命名空間所使用的解析器;schemaLocation中的http://www.wt.com/schema/user.xsd指定文件所在目錄,看下面META-INF/Spring.schemas的配置,指定了xsd的文件所在目錄。

# META-INF/Spring.handlers
# 定義指定namespace解析器,格式namespace = NamespaceHandler
http\://www.wt.com/schema/user=com.wt.test.customtag.MyNamespaceHandler
# META-INF/Spring.schemas
# 定義xml約束文檔xsd所在目錄
http\://www.wt.com/schema/user.xsd=META-INF/user.xsd
<!-- META-INF/user.xsd -->
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.wt.com/schema/user"
        elementFormDefault="qualified">

    <element name="user">
        <complexType>
            <attribute name="id" type="string" />
            <attribute name="email" type="string" />
            <attribute name="name" type="string" />
            <attribute name="age" type="int" />
        </complexType>
    </element>
</schema>
//注冊自定義的命名空間解析器
public class MyNamespaceHandler extends NamespaceHandlerSupport {
	public void init() {
        //注冊解析器
		registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
	}
}

//該方法在父類NamespaceHandlerSupport中,父類實(shí)現(xiàn)了NamespaceHandler
public BeanDefinition parse(Element element, ParserContext parserContext) {
	BeanDefinitionParser parser = findParserForElement(element, parserContext);
	return (parser != null ? parser.parse(element, parserContext) : null);
}
//標(biāo)簽解析器
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

	@Override
	protected Class<?> getBeanClass(Element element) {
		return User.class;
	}

	@Override
	protected void doParse(Element element, BeanDefinitionBuilder bean) {
		String name = element.getAttribute("name");
		String age = element.getAttribute("age");
		String email = element.getAttribute("email");
		if (StringUtils.hasText(name)) {
			bean.addPropertyValue("name", name);
		}
		if (StringUtils.hasText(age)) {
			bean.addPropertyValue("age", age);
		}
		if (StringUtils.hasText(email)) {
			bean.addPropertyValue("email", email);
		}
	}
}

以上就是所有的自定義標(biāo)簽的配置。實(shí)現(xiàn)NamespaceHandler,在init方法中注冊我們自定義的解析器。其實(shí)邏輯只是我們調(diào)用namespaceHandler的parse方法來解析自定義標(biāo)簽。但真正的解析肯定就是在parse方法中調(diào)用我們自己的解析器來解析而已。

現(xiàn)在開始分析,自定義標(biāo)簽是通過delegate.parseCustomElement(ele)來解析的。dalegate就是注釋中提到的創(chuàng)建的默認(rèn)的BeanDefinitionParserDelegate。

//BeanDefinitionParserDelegate.java
public BeanDefinition parseCustomElement(Element ele) {
	return parseCustomElement(ele, null);
}

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	//獲取namespaceURI,也就是我們定義的命名空間http://www.wt.com/schema/user
    String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
		return null;
	}
    //使用nameSpaceHandlerResolver,根據(jù)namespaceUri獲取自定義的標(biāo)簽解析器NamespaceHandler,
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
    //解析自定義標(biāo)簽
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

我們自定義的MyNamespaceHandler是就是通過NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);這一行來獲取的。直接跟進(jìn)去,進(jìn)入resolve方法。至于getNamespaceHandlerResolver()在哪里創(chuàng)建的之前提到過,這里就不繼續(xù)說了,是默認(rèn)的。

//DefaultNamespaceHandlerResolver.java
public NamespaceHandler resolve(String namespaceUri) {
	//獲取所有的命名空間->命名空間解析器的映射,就是Spring.handlers中的映射
	Map<String, Object> handlerMappings = getHandlerMappings();
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	if (handlerOrClassName == null) {
		return null;
	}
	//第一次加載的時(shí)候是String->String,但是如果已經(jīng)獲取過,就會實(shí)例化而變成String->NamespaceHandler并加入緩存,
	//所以這里先判斷是不是已經(jīng)初始化過,如果是就不用再初始化了
	else if (handlerOrClassName instanceof NamespaceHandler) {
		return (NamespaceHandler) handlerOrClassName;
	}
	else {
		//第一次加載,根據(jù)全限定類名初始化
		String className = (String) handlerOrClassName;
		try {
			Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
			//...
			NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            //執(zhí)行init方法,我們自定義的namespaceHandler在這個(gè)方法中注冊了我們自己的解析器
			namespaceHandler.init();
			//加入當(dāng)前實(shí)例,避免下次進(jìn)入再次實(shí)例化
			handlerMappings.put(namespaceUri, namespaceHandler);
			return namespaceHandler;
		}
		//...
	}
}

private Map<String, Object> getHandlerMappings() {
	Map<String, Object> handlerMappings = this.handlerMappings;
	if (handlerMappings == null) {
		synchronized (this) {
			handlerMappings = this.handlerMappings;
			if (handlerMappings == null) {
				//...
				try {
					//從文件中獲取映射關(guān)系
					Properties mappings =
						PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					//...
					handlerMappings = new ConcurrentHashMap<>(mappings.size());
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				//...
			}
		}
	}
	return handlerMappings;
}

public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

public DefaultNamespaceHandlerResolver() {
	this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

在getHandlerMappings()方法中,我們注意看這行代碼Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);這樣代碼就是從Spring.handlers文件中獲取所有的映射關(guān)系。在上面的代碼最后面,貼上了默認(rèn)的地址,也就是META-INF/Spring.handlers。

這樣就拿到了我們自定義的NamespaceHandler,接下來就是調(diào)用我們的MyNameSpaceHandler的parse方法來解析咯(實(shí)際上在這里頭會調(diào)用我們自己的解析器UserBeanDefinitionParser哦,比較簡單,自己看咯)。

我們熟悉的spring-aop就是通過自定義標(biāo)簽來實(shí)現(xiàn)的哦,使用的是AopNamespaceHandler哦。
還有springmvc的<annotation-driven/>也是哦,使用的是MvcNamespaceHandler哦。

xml的解析內(nèi)容比較少,但是如果不熟悉spring的話看起來還是有一定難度的。這里重在給自己勾勒初一個(gè)大致的輪廓,在這其中可以回憶起其中的細(xì)節(jié)。

關(guān)于如何進(jìn)行的加載及配置文件的解析問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI