Spring源码(二)-XML文件的读取-BeanDefinitionReader

前言

我们已经知道Spring会将 <bean/>@Bean@Component 等方式所定义的 Bean,最终都会被解析为 BeanDefinition 对象。那 Spring 是怎么读取 xml 配置文件或者说是怎么解析被 @Bean 等注解标注的方法或类并最终成了BeanDefinition呢?(注解方式之后再说)

BeanDefinitionReader

BeanDefinitionReader 的作用是读取 Spring配置文件中的内容,将之解析为BeanDefinition并注册到 BeanDefinitionRegistry 工厂中。


接下来就一起来看看 BeanDefinitionReader 的定义

public interface BeanDefinitionReader { 

	//返回用于注册bean定义的bean工厂
	BeanDefinitionRegistry getRegistry();

	//资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
	@Nullable
	ResourceLoader getResourceLoader();

	//返回类加载器。
	@Nullable
	ClassLoader getBeanClassLoader();

	//BeanName生成器
	//为没有明确指定bean名称的Bean生成一个名称
	BeanNameGenerator getBeanNameGenerator();

	//从指定的资源加载bean定义,返回找到的bean定义的数量
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

	//指定多个资源加载bean定义,返回找到的bean定义的数量
	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

	//从指定的资源位置加载bean定义
	//该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

	//加载多个配置文件路径
	int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

BeanDefinitionReader实现类

BeanDefinitionReader是一个接口,它有多个实现类,那我们看看它一共有多少个子类吧!
image.png
可以看到 BeanDefinitionReader 下有一个抽象子类 AbstractBeanDefinitionReaderAbstractBeanDefinitionReader下有三个子类。

再来看一张继承关系图:
image.png

  • AbstractBeanDefinitionReader:为 BeanDefinitionReader 接口的抽象实现类,实现了 EnvironmentCapable,提供了获取/设置环境的方法
  • XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition
  • PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition
  • GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean

AbstractBeanDefinitionReader

该类实现了 BeanDefinitionReaderEnvironmentCapable 接口的抽象类,提供属性:注册 bean 定义的bean 工厂、资源加载器、加载 bean 类的类加载器、环境、BeanName 生成器。具体定义如下:

//注册bean定义的bean工厂
    private final BeanDefinitionRegistry registry;
//资源加载器
	@Nullable
	private ResourceLoader resourceLoader;
//加载 bean 类的类加载器
	@Nullable
	private ClassLoader beanClassLoader;
//环境
	private Environment environment;
//BeanName生成器
	private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;

该类最核心的方法是 loadBeanDefinitions()方法,所以接下来我们主要就是分析该方法:

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int count = 0;
    String[] var3 = locations;
    int var4 = locations.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        String location = var3[var5];
        count += this.loadBeanDefinitions(location);
    }

    return count;
}

当传入的参数为资源位置数组时,进入上述方法,遍历调用 loadBeanDefinitions(location)方法。其定义如下:

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return this.loadBeanDefinitions(location, (Set)null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //获取资源加载器
    ResourceLoader resourceLoader = this.getResourceLoader();
  // 判断资源加载器是否为空,为空抛出异常
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    } else {
        int count;
        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                //根据资源路径调用ResourcePatternResolver的getResources方法,此方法可以加载多个资源
                Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                //根据资源来加载bean定义,调用本类loadBeanDefinitions(Resource... resources)方法
                count = loadBeanDefinitions(resource);
                //方法参数,由上层传入
                if (actualResources != null) {
                    Collections.addAll(actualResources, resources);
                }

                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }

                return count;
            } catch (IOException var6) {
                throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6);
            }
        } else {
            //此方法只能加载一个资源
            Resource resource = resourceLoader.getResource(location);
           //调用父类的loadBeanDefinitions(resources)方法,然后走不同的子类实现
            count = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }

            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }

            return count;
        }
    }
}

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int count = 0;
	for (Resource resource : resources) {
//循环调用父类loadBeanDefinitions(resources)方法,然后走不同的子类实现
		count += loadBeanDefinitions(resource);
	}
	return count;
}

根据资源加载器的不同,来处理资源路径,从而返回多个或一个资源,然后再将资源作为参数传递给loadBeanDefinitions(Resource... resources)方法,该方法用于处理多个资源,归根结底,最后还是调用 BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理

XmlBeanDefinitionReader

该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。具体使用如下:

public static void main(String[] args) {
       //设置加载环境
		//System.setProperty("spring.profiles.active", "dev");

		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		// 指定具体子类
		AbstractBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		int i = reader.loadBeanDefinitions("spring-config.xml");
		System.out.println("本次加载的Bean数量:" + i);
		// 根据BeanName获取Bean
		User user = (User)factory.getBean("user");
		System.out.println("user" + user);
		User.Vip vip = user.getVip();
		System.out.println("vip:" + vip.getVipLevel());

		//根据别名获取Bean
		User user2 = (User)factory.getBean("user2");
		System.out.println("user2:" + user2);
		User user4 = (User)factory.getBean("user4");
		System.out.println("user4:" + user4);
		//根据bean名称获取别名数组
		String[] users = factory.getAliases("user");
		for (String s : users) {
			System.out.println(s);
		}
	}
结果:====
本次加载的Bean数量:1
userUser{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
vip:9
user2:User{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
user4:User{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
user2
user3
user4
user7
user5
user6
  • User对象
public class User {
	public User() {
	}

	public User(String name, String address) {
		this.name = name;
		this.address = address;
	}

	@Override
	public String toString() {
		return "User{" +
				"name='" + name + '\'' +
				", address='" + address + '\'' +
				", vip=" + vip +
				'}';
	}

	private String name;
	private String address;
	private Vip vip;
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Vip getVip() {
		return vip;
	}

	public void setVip(Vip vip) {
		this.vip = vip;
	}


	// 内部Bean测试
	public class Vip {
		public String vipLevel;

		public String getVipLevel() {
			return vipLevel;
		}

		public void setVipLevel(String vipLevel) {
			this.vipLevel = vipLevel;
		}
	}
}
  • spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!--可以根据 , 、,; 、;进行分割-->
	<bean  class="com.gongj.bean.User" name="user2,user5;user6,;user7" id="user">
		<property name="vip">
			<bean id="inner" class="com.gongj.bean.User$Vip">
				<constructor-arg ref="user"/>
				<property name="vipLevel" value="9"/>
			</bean>
		</property>
	</bean>
	<alias name="user2" alias="user3"></alias>
	<alias name="user2" alias="user4"></alias>

	<!--<beans profile="dev,;prd">
		<bean id="user" class="com.gongj.bean.User" name="user2,;user5"></bean>
	<alias name="user2" alias="user3"></alias>
	<alias name="user2" alias="user4"></alias>
	</beans>-->
</beans>
  • pom.xml
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>5.2.6.RELEASE</version>
</dependency>

接下来就开始我们的重头戏,XmlBeanDefinitionReader读取 xml 文件配置。


从我们写的这段代码进入 reader.loadBeanDefinitions("spring-config.xml"),上面已经提到最终会调用BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理,而我们指定的子类实现为XmlBeanDefinitionReader,调试进入子类实现,可以看到它加载Resource并封装为EncodedResource,并指定编码,当然它的 encoding、 charset 都是null。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource)); >1
    }

 public EncodedResource(Resource resource) {
        this(resource, (String)null, (Charset)null);
    }

然后进入到loadBeanDefinitions(EncodedResource encodedResource)方法,该方法主要根据encodedResource获得InputSource对象,并将InputSource对象参数和Resource对象参数传递给下层方法。

//当前正在加载的XML bean定义资源,使用ThreadLocal ,这样可以避免资源重复加载
	private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
			new NamedThreadLocal<>("XML bean definition resources currently being loaded");


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}
		//获得当前正在加载的XML bean定义资源
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			//分配空间,并将其添加进去
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//判断currentResources中是否包含encodedResource,如果有则抛出异常,没有则加入
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			//获取Resource对应的字节流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				//inputStream 流转为一个 InputSource 对象
				InputSource inputSource = new InputSource(inputStream);
				//如果资源有编 石马 格式,那就给 inputSource 对象也设置上编石马格式
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//该方法做进一步的解析操作,也是创建BeanDefinition的关键
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); >1
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			//移除当前正在加载的XML bean定义资源
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}

然后进入doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,该方法主要是将xml 资源文件转换为Document对象并根据Dcoument对象注册BeanDefinition

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
            //将资源文件解析成 Document 对象
			Document doc = doLoadDocument(inputSource, resource); >1
           //根据返回的Dcoument注册Bean信息
			int count = registerBeanDefinitions(doc, resource); >2
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from " + resource, ex);
		}
	}

我们先来看一下 doLoadDocument(InputSource inputSource, Resource resource)方法:

//定义从资源文件加载 到 转换为 Document 的功能
private DocumentLoader documentLoader = new DefaultDocumentLoader();

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware()); >1
	}

doLoadDocument 方法里最终调用的是 documentLoader.loadDocument ()方法。
该方法的调用,一共需要五个参数:

  • InputSource:要调用的资源文件。
  • EntityResolver: 处理文件的验证方式。
  • ErrorHandler: 错误处理器。
  • validationMode: XML 文件的验证模式。
  • namespaceAware: 是否开启自动感知名称空间。

documentLoader.loadDocument ()点击会进入到DocumentLoader接口,该接口下只有一个实现:DefaultDocumentLoader
具体的调用如下:

@Override
	public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
			ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
		//创建DocumentBuilderFactory对象
		DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); >1
		if (logger.isTraceEnabled()) {
			logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
		}
		//创建DocumentBuilder对象
		DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); >2
		//将inputSource解析为Document对象
		return builder.parse(inputSource);
	}

这里就会将 XML 里的配置解析为 Document对象。使用这个Document对象可以获取 XML 文件中的节点并且创建节点。

createDocumentBuilderFactory(validationMode, namespaceAware):创建DocumentBuilderFactory对象

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
			throws ParserConfigurationException {
		// 1、获取DocumentBuilderFactory实例
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(namespaceAware);
		// 2、如果开启xml验证的话,则验证xml
		if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
			factory.setValidating(true);
			// 如果xml验证模式为XSD则需要强制指定由此代码生成的解析器将提供对XML名称空间的支持
			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;
	}

createDocumentBuilder(factory, entityResolver, errorHandler):创建DocumentBuilder对象

protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
			@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
			throws ParserConfigurationException {
		// 创建DocumentBuilder对象
		DocumentBuilder docBuilder = factory.newDocumentBuilder();
		// 如果entityResolver不为空,则给docBuilder设置entityResolver
		if (entityResolver != null) {
			docBuilder.setEntityResolver(entityResolver);
		}
		// 如果errorHandler不为空,则给docBuilder设置errorHandler
		if (errorHandler != null) {
			docBuilder.setErrorHandler(errorHandler);
		}
		return docBuilder;
	}

接下来我们看registerBeanDefinitions(doc, resource)方法

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建BeanDefinitionDocumentReader对象,完成 BeanDefinition 的解析和注册
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	    //之前的bd数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		// 注册bd
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); >1
		// 本次注册的bd数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

这个方法中重点关注documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法。点击进入就会到 BeanDefinitionDocumentReader接口,该接口的作用就是定义读取 Docuemnt 并注册 BeanDefinition

该接口就一个实现类 DefaultBeanDefinitionDocumentReader,接下来就看 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions 方法。

	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		doRegisterBeanDefinitions(doc.getDocumentElement()); >1
	}

该方法有两个入参:

  • Document:代指 Spring 的配置文件信息,通过 BeanDefinitionReader 解析 Resrouce 实例得到。

  • XmlReaderContext :主要包含了 BeanDefinitionReaderResrouce

然后进入doRegisterBeanDefinitions(doc.getDocumentElement())方法,进行解析

protected void doRegisterBeanDefinitions(Element root) {
		// 创建 delegate 对象,解析 Element 的各种方法
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);
		// 验证 XML 文件的命名空间,
		// 即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
		if (this.delegate.isDefaultNamespace(root)) {
			// 获取profile属性的值  <beans profile="dev,;prd"></beans>
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				//将字符串按照指定的字符转换成String[]数组,如字符串中不包含指定字符,则将整个字符串放进数组。
				//如指定字符有多个,是分别按单个字符来切割的。
				//字符串: "gong-jie/yuan"
				//	 *
				//	 * 指定字符: "-/"
				//	 * 返回数组:[gong, jie, yuan]
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// 判断你指定的环境是否与加载的配置文件的环境一致,如果不一致直接返回
				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); >1
		// 后置空方法
		postProcessXml(root);
		this.delegate = parent;
	}

我们来看其中最重要的方法 parseBeanDefinitions(root, this.delegate);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//是否是DefaultNamespace
		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;
					// 如果符合Spring的命名规则,对该标签进行解析。
					// 实例 <bean id="user" class="com.gongj.bean.User"></bean>
					if (delegate.isDefaultNamespace(ele)) {
						parseDefaultElement(ele, delegate); >1
					}
					else {
						// 解析用户自定义的规则
						// <tx:annotation-driven/>
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
                // 解析用户自定义的规则   
			delegate.parseCustomElement(root);
		}
	}

而两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置。Spring 当然知道该怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置,对于根节点或者子节点如果是默认命名空间的话,采用 parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement 方法对自定义命名空间进行解析。 而判断是否是默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI获取命名空间,并与 Spring 中固定的命名空间http://www.springframework.org/scherna/beans 进行比对,如果一致则认为是默认,否则就认为是自定义。


默认标签的解析是在 parseDefaultElement(ele, delegate) 函数进行的, 函数中的功能一目了然 ,分别对四种标签(import,alias,bean,beans)做了不同的处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// 解析import标签  <import resource="spring-config.xml"></import>
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// 解析alias标签 <alias name="user" alias="user2"></alias>
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// 解析bean标签 <bean id="user" class="com.gongj.bean.User"></bean>
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate); >1
		}
		// 解析beans标签
		/**
		 * <beans profile="dev">
		 * 		<bean id="user" class="com.gongj.bean.User"></bean>
		 * 		<import resource="spring-config.xml"></import>
		 * 		<alias name="user" alias="user2"></alias>
		 * 	</beans>
		 */
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			//调用doRegisterBeanDefinitions,再次重复解析xml的过程
			doRegisterBeanDefinitions(ele);
		}
	}

BeanDefinitionReader 这个源码阅读就到这啦!下面将拆分几篇博文介绍各种标签的解析。


参考文献:

Spring IoC之BeanDefinitionReader

BeanDefinitionReader-将xml解析到BeanDefinition的执行者

18–Spring注册BeanDefinition

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值