IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring的XML解析原理,这一次全搞懂再走! -> 正文阅读

[Java知识库]Spring的XML解析原理,这一次全搞懂再走!

		if (parentEnvironment instanceof ConfigurableEnvironment) {
			getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
		}
	}
}

然后解析传入的相对路径保存到configLocations变量中,最后再调用父类AbstractApplicationContext的refresh方法刷新容器(启动容器都会调用该方法),我们着重来看这个方法:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { //为容器初始化做准备 prepareRefresh(); // 解析xml ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);

		try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh();
		}

		catch (BeansException ex) {
			if (logger.isWarnEnabled()) {
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			} // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex;
		}

		finally { // Reset common introspection caches in Spring's core, since we  // might not ever need metadata for singleton beans anymore... resetCommonCaches();
		}
	}
}

这个方法是一个典型的模板方法模式的实现,第一步是准备初始化容器环境,这一步不重要,重点是第二步,创建BeanFactory对象、加载解析xml并封装成BeanDefinition对象都是在这一步完成的。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}


点进去看是调用了refreshBeanFactory方法,但这里有两个实现,应该进哪一个类里面呢?

![image.png](https://upload-images.jianshu.io/upload_images/24613101-e23e2aef7978a0c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


如果你还记得前面的继承体系,那你就会毫不犹豫的进入AbstractRefreshableApplicationContext类中,所以在阅读源码的过程中一定要记住类的继承体系。

protected final void refreshBeanFactory() throws BeansException { //如果BeanFactory不为空,则清除BeanFactory和里面的实例 if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try { //创建DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId()); //设置是否可以循环依赖 allowCircularReferences //是否允许使用相同名称重新注册不同的bean实现. customizeBeanFactory(beanFactory); //解析xml,并把xml中的标签封装成BeanDefinition对象 loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}


在这个方法中首先会清除掉上一次创建的BeanFactory和对象实例,然后创建了一个DefaultListableBeanFactory对象并传入到了loadBeanDefinitions方法中,这也是一个模板方法,因为我们的配置不止有xml,还有注解等,所以这里我们应该进入AbstractXmlApplicationContext类中:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //创建xml的解析器,这里是一个委托模式 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context’s // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的 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);
}


首先创建了一个XmlBeanDefinitionReader对象,见名知意,这个就是解析xml的类,需要注意的是该类的构造方法接收的是BeanDefinitionRegistry对象,而这里将DefaultListableBeanFactory对象传入了进去(别忘记了这个对象是实现了BeanDefinitionRegistry类的),如果你足够敏感,应该可以想到后面会委托给该类去注册。注册什么呢?自然是注册BeanDefintion。记住这个猜想,我们稍后来验证是不是这么回事。 接着进入loadBeanDefinitions方法获取之前保存的xml配置文件路径,并委托给XmlBeanDefinitionReader对象解析加载:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
} //获取需要加载的xml配置文件 String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}


最后会进入到抽象父类AbstractBeanDefinitionReader中:

public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException { // 这里获取到的依然是DefaultListableBeanFactory对象 ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
“Cannot load bean definitions from location [” + location + “]: no ResourceLoader available”);
}

	if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //把字符串类型的xml文件路径,形如:classpath*:user/**/*-context.xml,转换成Resource对象类型,其实就是用流  //的方式加载配置文件,然后封装成Resource对象 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //主要看这个方法 int count = loadBeanDefinitions(resources);
			if (actualResources != null) {
				Collections.addAll(actualResources, resources);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
			}
			return count;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location);
		int count = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
		}
		return count;
	}
}

这个方法中主要将xml配置加载到存中并封装成为Resource对象,这一步不重要,可以略过,主要的还是loadBeanDefinitions方法,最终还是调用到子类XmlBeanDefinitionReader的方法:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
try { //获取Resource对象中的xml文件流对象 InputStream inputStream = encodedResource.getResource().getInputStream();
try { //InputSource是jdk中的sax xml文件解析对象 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 { //把inputSource 封装成Document文件对象,这是jdk的API Document doc = doLoadDocument(inputSource, resource); //主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // 创建DefaultBeanDefinitionDocumentReader对象,并委托其做解析注册工作 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	int countBefore = getRegistry().getBeanDefinitionCount(); //主要看这个方法,需要注意createReaderContext方法中创建的几个对象 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

public XmlReaderContext createReaderContext(Resource resource) { // XmlReaderContext对象中保存了XmlBeanDefinitionReader对象和DefaultNamespaceHandlerResolver对象的引用,在后面会用到 return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}

接着看看DefaultBeanDefinitionDocumentReader中是如何解析的:

protected void doRegisterBeanDefinitions(Element root) { // 创建了BeanDefinitionParserDelegate对象 BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent); // 如果是Spring原生命名空间,首先解析 profile标签,这里不重要 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;
}
}
}

	preProcessXml(root); //主要看这个方法,标签具体解析过程 parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

	this.delegate = parent;
}

在这个方法中重点关注preProcessXml、parseBeanDefinitions、postProcessXml三个方法,其中preProcessXml和postProcessXml都是空方法,意思是在解析标签前后我们自己可以扩展需要执行的操作,也是一个模板方法模式,体现了Spring的高扩展性。然后进入parseBeanDefinitions方法看具体是怎么解析标签的:

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);
}
}


这里有两种标签的解析:Spring原生标签和自定义标签。怎么区分这两种标签呢?

// 自定义标签 context:component-scan/ // 默认标签 bean:/


如上,带前缀的就是自定义标签,否则就是Spring默认标签,无论哪种标签在使用前都需要在Spring的xml配置文件里声明Namespace URI,这样在解析标签时才能通过Namespace URI找到对应的NamespaceHandler。

xmlns:context=“http://www.springframework.org/schema/context”

http://www.springframework.org/schema/beans


isDefaultNamespace判断是不是默认标签,点进去看看是不是跟我上面说的一致:

public boolean isDefaultNamespace(Node node) {
return isDefaultNamespace(getNamespaceURI(node));
}

public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";
public boolean isDefaultNamespace(@Nullable String namespaceUri) {
	return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));
}

可以看到http://www.springframework.org/schema/beans所对应的就是默认标签。接着,我们进入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);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele);
}
}


这里面主要是对import、alias、bean标签的解析以及beans的字标签的递归解析,主要看看bean标签的解析:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 解析elment封装为BeanDefinitionHolder对象 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) { // 该方法功能不重要,主要理解设计思想:装饰者设计模式以及SPI设计思想 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try { // 完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // 获取id和name属性 String id = ele.getAttribute(ID_ATTRIBUTE);
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 获取别名属性,多个别名可用,;隔开 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");
		}
	} //检查beanName是否重复 if (containingBean == null) {
		checkNameUniqueness(beanName, aliases, ele);
	} // 具体的解析封装过程还在这个方法里 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		if (!StringUtils.hasText(beanName)) {
			try {
				if (containingBean != null) {
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				} else {
					beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible,  // if the generator returned the class name plus a suffix.  // This is expected for Spring 1.2/2.0 backwards compatibility. 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;
} // bean的解析 public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {

	this.parseState.push(new BeanEntry(beanName)); // 获取class名称和父类名称 String className = null;
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	String parent = null;
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);
	}

	try { // 创建GenericBeanDefinition对象 AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		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中的constructor-arg标签 parseConstructorArgElements(ele, bd); //解析bean中的property标签  parsePropertyElements(ele, bd);

		parseQualifierElements(ele, bd);

		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));

		return bd;
	}

	return null;
}

bean标签的解析步骤仔细理解并不复杂,就是将一个个标签属性的值装入到了BeanDefinition对象中,这里需要注意parseConstructorArgElements和parsePropertyElements方法,分别是对constructor-arg和property标签的解析,解析完成后分别装入了BeanDefinition对象的constructorArgumentValues和propertyValues中,而这两个属性在接下来c和p标签的解析中还会用到,而且还涉及一个很重要的设计思想——装饰器模式。 Bean标签解析完成后将生成的BeanDefinition对象、bean的名称以及别名一起封装到了BeanDefinitionHolder对象并返回,然后调用了decorateBeanDefinitionIfRequired进行装饰:

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {

	BeanDefinitionHolder finalDefinition = definitionHolder; //根据bean标签属性装饰BeanDefinitionHolder,比如<bean class="xx" p:username="dark"/> NamedNodeMap attributes = ele.getAttributes();
	for (int i = 0; i < attributes.getLength(); i++) {
		Node node = attributes.item(i);
		finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
	} //根据bean标签子元素装饰BeanDefinitionHolder\ 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;
}

在这个方法中分别对Bean标签的属性和子标签迭代,获取其中的自定义标签进行解析,并装饰之前创建的BeanDefinition对象,如同下面的c和p:

// c:和p:表示通过构造器和属性的setter方法给属性赋值,是constructor-arg和property的简化写法


两个步骤是一样的,我们点进decorateIfRequired方法中:

public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { //根据node获取到node的命名空间,形如:http://www.springframework.org/schema/p String namespaceUri = getNamespaceURI(node);
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { // 根据配置文件获取namespaceUri对应的处理类,SPI思想 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) { //调用NamespaceHandler处理类的decorate方法,开始具体装饰过程,并返回装饰完的对象 BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 08:53:50  更:2021-08-28 08:54:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:05:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码