Spring源码深度解析1---XML加载

该文详细介绍了Spring框架中,基于XML配置的bean如何从定义、加载到实例化的全过程。首先通过ClassPathXmlApplicationContext加载配置文件,然后解析XML,创建BeanDefinition,最后注册并实例化bean。整个流程涉及资源加载、XML解析、BeanDefinition注册等多个关键步骤。
摘要由CSDN通过智能技术生成

参考 《Spring源码深度解析》 当前使用spring版本为 5.0.6 仅作为个人阅读笔记使用

1、入门

  • 定义一个简单的bean
public class MyTestBean {
   private String testStr = "testStr";

   public String getTestStr() {
       return testStr;
   }
   public void setTestStr(String testStr) {
       this.testStr = testStr;
   }
}
  • 在xml中声明这个bean
<?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 id = "testBean" class="com.xd.study.spring.beanLife.MyTestBean"></bean>
</beans>
  • 编写测试代码
public class BeanTestMain {
   public static void main(String[] args) {
       ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:BeanFactoryTest.xml");
       MyTestBean bean = (MyTestBean) ac.getBean("testBean");
       System.out.println(StringUtils.equals("testStr", bean.getTestStr()));
   }
}
  • 输出结果为: true

2、功能分析

2.1 上述测试代码完成以下几点功能

  1. 读取配置文件beanFactoryTest.xml
  2. 根据beanFactoryTest.xml中的配置找到对应的类,进行实例化
  3. 调用实例化后的实例

2.2 XML的加载

ApplicationContext ac = new ClassPathXmlApplicationContext(“classpath:BeanFactoryTest.xml”);

ClassPathXmlApplicationContext.java

	/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 调用父类构造方法,进行相关的对象创建等操作,包含属性的赋值操作
		super(parent);
		// 设置配置文件的路径,方便后续的加载和读取
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

AbstractApplicationContext.java (重要)

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			// 刷新前的预处理,主要是一些系统属性、环境变量的设置和校验
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			// 创建容器对象:DefaultListableBeanFactory
			// 加载xml配置文件的属性值到当前工厂, 最重要的就是 BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// beanFactory的准备工作,填充属性并设置(比如context的类加载器,BeanPostProcessor和XXXAware自动装配等)
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 子类覆盖方法做额外的处理,这里我们自己一般不做任何扩展工作
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 调用BeanFactoryPostProcessors,将结果存入参数beanFactory中
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 注册BeanPostProcessors,这里只是注册,真正的调用的是getBean中的doGetBean方法
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 初始化消息源组件(做国际化功能;消息绑定,消息解析)
				initMessageSource();

				// Initialize event multicaster for this context.
				// 初始化事件监听多路广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 留给子类来初始化其他的bean(springboot内置tomcat有用到...)
				onRefresh();

				// Check for listener beans and register them.
				// 在所有注册的bean中找实现了ApplicationListener接口的监听器bean,注册到消息广播(ApplicationEventMulticaster)中
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 实例化剩下的单实例(非懒加载)bean的生命周期(进行bean对象的创建工作)
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 完成刷新,通知生命周期处理器刷新过程(LifecycleProcessor.onRefresh()方法),并发布事件(ContextRefreshedEvent)
				finishRefresh();
			}

			......
		}
	}
2.2.1 资源加载流程
  1. refresh方法中 obtainFreshBeanFactory()
  2. obtainFreshBeanFactoryrefreshBeanFactory()
  3. 子类 AbstractRefreshableApplicationContext中实现的refreshBeanFactory方法里有loadBeanDefinitions(beanFactory)
  4. 子类 AbstractXmlApplicationContext 中实现的loadBeanDefinitions方法里有loadBeanDefinitions(beanDefinitionReader)
  5. 因为入门案例中为xml文件 最终调用方法是XmlBeanDefinitionReader中的loadBeanDefinitions(EncodedResource encodedResource)

XmlBeanDefinitionReader.java

	/**
	 * Load bean definitions from the specified XML file.
	 * @param encodedResource the resource descriptor for the XML file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
		// 通过属性来记录已经加载的资源
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			// 从encodedResource中获取已经封装的Resource对象并再次从Resource中获取inputStream
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				// InputSource这个类不来自于Spring
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				// 最终调用 真正的逻辑核心部分
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}


	/**
	 * Actually load bean definitions from the specified XML file.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			// 加载XML文件,并得到对应的Document
			Document doc = doLoadDocument(inputSource, resource);
			// 根据返回document注册bean信息
			return registerBeanDefinitions(doc, resource);
		}
		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);
		}
	}


	/**
	 * Actually load the specified document using the configured DocumentLoader.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the DOM Document
	 * @throws Exception when thrown from the DocumentLoader
	 * @see #setDocumentLoader
	 * @see DocumentLoader#loadDocument
	 */
	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}

getValidationModeForResource(resource) 获取对XML文件的验证模式

如果想了解XML的验证模式和Document的读取 可阅读 《Spring源码深度解析》的 2.6节、2.7节

2.2.2 解析XML
	/**
	 * Register the bean definitions contained in the given DOM document.
	 * Called by {@code loadBeanDefinitions}.
	 * <p>Creates a new instance of the parser class and invokes
	 * {@code registerBeanDefinitions} on it.
	 * @param doc the DOM document
	 * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
	 */
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		// 记录统计前BeanDefinition的加载个数
		int countBefore = getRegistry().getBeanDefinitionCount();
		// 加载及注册bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		// 记录本次加载的BeanDefinition个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}


	/**
	 * This implementation parses bean definitions according to the "spring-beans" XSD
	 * (or DTD, historically).
	 * <p>Opens a DOM Document; then initializes the default settings
	 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
	 */
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root);
	}


	/**
	 * 真正开始解析
	 * Register each bean definition within the given root {@code <beans/>} element.
	 */
	protected void doRegisterBeanDefinitions(Element root) {
		// 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 = createDelegate(getReaderContext(), root, parent);

		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);
				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);
		parseBeanDefinitions(root, this.delegate);
		// 解析后处理,留给子类实现(模版方法模式)
		postProcessXml(root);

		this.delegate = parent;
	}


	/**
	 * 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) {
		// 对Bean的处理
		// 默认Bean <bean id = "test" class = "test.TestBean"/>
		// 自定义Bean <tx:annotation-driven/>
		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)) {
						// 对Bean的处理(默认)
						parseDefaultElement(ele, delegate);
					}
					else {
						// 对Bean的处理(自定义)
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}


	/**
	 * 对Bean的处理
	 */
	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)) {
			// 注册解析的BeanDefinitions
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

如果想了解Bean标签的解析 可阅读 《Spring源码深度解析》的 第三章

2.2.3 注册解析的BeanDefinitions

	/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		/*
		 * 1. 提取元素中的id和name属性
 		 * 2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
 		 * 3. 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
         * 4. 将获取到的信息封装到BeanDefinitionHolder的实例中
		 */
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				// 注册BeanDefinition
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			// 通知监听器解析及注册完成(这里的实现只为扩展,当开发人员需要对注册的BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中。目前在Spring中并没有对此事件做任何逻辑处理)
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}


	/**
	 * Register the given bean definition with the given bean factory.
	 * @param definitionHolder the bean definition including name and aliases
	 * @param registry the bean factory to register with
	 * @throws BeanDefinitionStoreException if registration failed
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		// 使用beanName做唯一标识注册
		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 alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}


	/**
     * 通过beanName注册
     * 1、对AbstractBeanDefinition的校验。解析XMl的时候校验针对XMl格式,此时的校验针对AbstractBeanDefinition中methodOverrides属性的
     * 2、对beanName已经注册情况的处理。如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖
     * 3、加入map缓存
     * 4、清除解析之前留下的对应beanName缓存
     */
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				// 注册前最后一次校验 校验AbstractBeanDefinition中methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

		BeanDefinition oldBeanDefinition;

		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
		// 处理已经注册beanName的情况
		if (oldBeanDefinition != null) {
			// 如果对应的beanName已经注册并且在配置中配置了bean不允许覆盖,则抛出异常
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
				// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				// 因为beanDefinitionMap是全局变量,这里定会存在并发访问的情况
				synchronized (this.beanDefinitionMap) {
					// 注册beanDefinition
					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
				// 注册beanDefinition
				this.beanDefinitionMap.put(beanName, beanDefinition);
				// 记录beanName
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			// 重置所有beanName对应的缓存
			resetBeanDefinition(beanName);
		}
	}


	/**
     * 别名注册
     * 1、如果beanName与alias相同的话 不记录alias并删除对应的alias
     * 2、alias覆盖处理。若aliasName已经使用并已经指向了另一个BeanName 则需要用户的设置进行处理
     * 3、alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常
     * 4、注册alias
     */
	public void registerAlias(String name, String alias) {
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		synchronized (this.aliasMap) {
			// 如果beanName与alias相同的话 不记录alias并删除对应的alias
			if (alias.equals(name)) {
				this.aliasMap.remove(alias);
			}
			else {
				String registeredName = this.aliasMap.get(alias);
				if (registeredName != null) {
					// 已存在 不处理
					if (registeredName.equals(name)) {
						// An existing alias - no need to re-register
						return;
					}
					// 如果alias不允许被覆盖则抛出异常
					if (!allowAliasOverriding()) {
						throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
								name + "': It is already registered for name '" + registeredName + "'.");
					}
				}
				// 当A->B存在时,若再次出现A->C->B时候则会抛出异常
				checkForAliasCircle(name, alias);
				this.aliasMap.put(alias, name);
			}
		}
	}
2.2.4 通知监听器解析及注册完成

通过代码 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
完成此工作,通知监听器解析及注册完成(这里的实现只为扩展,当开发人员需要对注册的BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中。目前在Spring中并没有对此事件做任何逻辑处理)

3、小结

只描述了大概流程 具体细节参考书《Spring源码深度解析》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值