Spring Ioc源码分析系列--Ioc源码入口分析
Spring Ioc源码分析系列--Ioc源码入口分析
本系列文章代码基于Spring Framework 5.2.x
前言
上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了Ioc的基础概念以及Spring Ioc体系的部分基础知识。那么这一篇就会真正通过一个例子,启动Ioc容器,获取容器里的bean。
首先说明,本文的例子是基于xml配置文件去完成的。
为什么是xml?因为xml是Spring的灵魂,可能我们初学Spring都会有畏难情绪,看到繁杂的xml就会打退堂鼓。但是实际上不然,xml的格式是相当清晰的,一个配置文件可以说没有一行配置是多余的。现在大部分的配置是用注解去完成的,相比xml而言是简洁许多,但是对于我们初学而言,xml其实是更好的方式,xml相对于注解而言是相对繁杂,但是它的信息也更多更明确,注解只是添加了一个注解就完成配置,细节上是更为隐蔽的。再加上xml配置文件和注解配置的原理是相通的,核心思想是一样的,掌握核心就万变不离其宗。所以这系列文章的例子大部分都会采取xml的方式去配置,当然后续可能也会补充一下注解方式的例子和分析文章。
万事开头难,如果实在觉得看不懂但又想学的,可以硬着头皮看下去,等以后回过头来再看的时候,会有豁然开朗的感觉。
源码分析
启动容器示例
废话少说,下面开始搞个例子分析一下。所有源码都在我的仓库ioc-sourcecode-analysis-code-demo里找到。
首先弄个实体类User
/**
 * @author Codegitz
 * @date 2022/4/26 10:58
 **/
public class User {
	private String id;
	private String name;
	private String age;
}
创建业务类UserService以及业务实现类UserServiceImpl,这里的逻辑很简单,就是根据传入的name和age返回一个新的user对象。
/**
 * @author Codegitz
 * @date 2022/4/26 10:59
 **/
public interface UserService {
	User getUser(String name,String age);
}
public class UserServiceImpl implements UserService {
	@Override
	public User getUser(String name, String age) {
		User user = new User();
		user.setId("1");
		user.setName(name);
		user.setAge(age);
		return user;
	}
}
业务类的准备工作已经完成了,接下来就是要写个xml配置文件,告诉Spring Ioc我需要一个UserService的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="userService" class="io.codegitz.service.impl.UserServiceImpl"/>
</beans>
这个xml配置文件比较简单,我们来解释一下每一行是什么意思。
<?xml version="1.0" encoding="UTF-8"?>为xml文件的规定头,没啥好说的。
xmlns="http://www.springframework.org/schema/beans"表明了xml的命名空间,xmlns全称为xml namespace,一个xml里面命名空间可以有多个。
xmlns:xsi和 xsi:schemaLocation是指明了xml文件的验证模型和验证模型文件的位置。可以看到验证模型能通过http方式获取,但是为了防止网络抖动的影响,一般会在本地存放验证文件。spring-beans.xsd就可以在图示的目录下找到。

接下来就到了<bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/>这一行,这是我们获取一个Bean的关键配置,这里告诉Ioc我需要一个id为userService,class为io.codegitz.service.impl.UserServiceImpl的Bean。
到这里配置已经完成了,接下来创建一个引导类启动Ioc就能获取到这个bean了。
新建引导类Application,通过ClassPathXmlApplicationContext类引导启动Ioc容器,这是加载xml配置的常用引导类。
/**
 * @author Codegitz
 * @date 2022/4/26 10:57
 **/
public class Application {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService) applicationContext.getBean("userService");
		User codegitz = userService.getUser("codegitz", "25");
		System.out.println(codegitz);
	}
}
到这里其实已经完成了所有样例的准备,可以启动容器了。

可以看到,只是简单的ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");就可以启动一个容器,随后就能通过getBean()方法获取容器里的bean了,接下来我们看看new ClassPathXmlApplicationContext("beans.xml")发生了什么。
容器启动分析
ClassPathXmlApplicationContext构造方法
这里我们使用的是ClassPathXmlApplicationContext,那么就从这个类的构造方法开始分析。
摘取的代码片段我会保留原文注释,看官可以细细品味。
	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocation resource location
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
继续跟进构造函数,根据方法上的注释和代码可以看到。这里就做了两件事,根据给定的xml配置文件加载BeanDefinition,然后调用refresh()方法刷新容器。
	/**
	 * 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);
		// 设置配置文件信息,这里会进行占位符的替换
		// 例如/${env}-beans.xml类型的路径会被占位符替换,如果env=sit,那么路径就会被替换为/sit-beans.xml
		setConfigLocations(configLocations);
		// 如果没有刷新,就会调用refresh()方法进行刷新,这个方法是整个Ioc的关键入口,由父类AbstractApplicationContext实现
		if (refresh) {
			refresh();
		}
	}
前面两个方法比较简单,可以跟着注释看一下。重点在refresh()方法。这个方法会完成Ioc所需要的所有配置加载,完成所有bean的注册以及Spring的一系列实现。
refresh()方法
到这里,才是真正摸到了Ioc的源码入口。看一下refresh()方法的代码。
	public void refresh() throws BeansException, IllegalStateException {
		//给容器refresh加锁,避免容器在refresh阶段时,容器进行了初始化或者销毁操作
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			//调用容器准备刷新的方法,获取容器当前时间,同时给容器设置同步标识、具体方法
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			/**
			 * 告诉子类启动refreshBeanFactory方法,refreshBeanFactory方法会载入bean的定义文件,该方法的实现会针对xml配置,最终创建内部容器
			 * 该容器负责bean的创建与管理,会进行BeanDefinition的注册
			 */
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			//注册一些容器中需要使用的系统bean,例如classloader,BeanFactoryPostProcessor等
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses
				// 允许容器的子类去注册PostProcessor,钩子方法.
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context.
				//激活容器中注册为bean的BeanFactoryPostProcessor
				//对于注解容器,org.springframework.context.annotation.ConfigurationClassPostProcessor
				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();
			}
		}
	}
咋一看代码量非常大,但是逻辑是比较清晰的,结合注释来看,再调试一遍,没啥大问题。
下面对方法进行逐个分析,鉴于一次性写完会比较长,本文先分析前三个方法,即prepareRefresh()、obtainFreshBeanFactory()和prepareBeanFactory(beanFactory)三个方法。
prepareRefresh()方法
跟进prepareRefresh()方法,这个方法主要是做了一些初始化属性设置和校验。
	/**
	 * Prepare this context for refreshing, setting its startup date and
	 * active flag as well as performing any initialization of property sources.
	 */
	protected void prepareRefresh() {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);
        // 省略部分日志...
		// Initialize any placeholder property sources in the context environment.
		// 初始化一些属性,交由子类实现
		initPropertySources();
		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		// 校验是否有必须的属性,如果有必须的属性但是环境没配置,则抛出异常
		getEnvironment().validateRequiredProperties();
		// Store pre-refresh ApplicationListeners...
		// 存储在refresh之前注册的ApplicationListener,避免这部分的ApplicationListener在后续的调用中被丢失
		// 这是一个2019年修复的bug
		if (this.earlyApplicationListeners == null) {
			this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
		}
		else {
			// Reset local application listeners to pre-refresh state.
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}
		// Allow for the collection of early ApplicationEvents,
		// to be published once the multicaster is available...
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}
这个里面比较简单,没啥好看。稍微来看下getEnvironment().validateRequiredProperties()的代码。
	/**
	 * Return the {@code Environment} for this application context in configurable
	 * form, allowing for further customization.
	 * <p>If none specified, a default environment will be initialized via
	 * {@link #createEnvironment()}.
	 */
	@Override
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
	/**
	 * Create and return a new {@link StandardEnvironment}.
	 * <p>Subclasses may override this method in order to supply
	 * a custom {@link ConfigurableEnvironment} implementation.
	 */
	protected ConfigurableEnvironment createEnvironment() {
		return new StandardEnvironment();
	}
可以看到这里返回的是StandardEnvironment类型Environment,这里的逻辑就是有则返回,无则创建。接下来看validateRequiredProperties()方法。最终实现是在AbstractPropertyResolver#validateRequiredProperties()方法。
	@Override
	public void validateRequiredProperties() {
		MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
		// 遍历requiredProperties,逐个去当前环境获取是否存在
		for (String key : this.requiredProperties) {
			if (this.getProperty(key) == null) {
				ex.addMissingRequiredProperty(key);
			}
		}
		// 如果存在缺失的必须属性,则抛出异常
		if (!ex.getMissingRequiredProperties().isEmpty()) {
			throw ex;
		}
	}
prepareRefresh()方法比较简单,到此已经基本看完,接下来看下一个obtainFreshBeanFactory()方法。
obtainFreshBeanFactory()方法
obtainFreshBeanFactory()方法从名字上就知道是获取并且同时刷新一个beanfactory。
在没有分析代码之前,先来猜测一下它的实现。我们知道它最终会返回一个可以使用的beanfactory,说明此时在beanfactory里已经初始化完成了我们定义的BeanDefinition,那么这里应该会有一些基础信息的配置,然后解析xml文件,获取xml配置信息,然后初始化相应的BeanDefinition,准备就绪后返回beanfactory。当然这只是猜测,具体实现如何,马上分析。
那么接下来就跟进这个方法看一下代码实现。
	/**
	 * Tell the subclass to refresh the internal bean factory.
	 * @return the fresh BeanFactory instance
	 * @see #refreshBeanFactory()
	 * @see #getBeanFactory()
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		// 调用子类的实现,刷新beanFactory
		refreshBeanFactory();
		// 返回刷新完成(即启动完成)的beanFactory
		return getBeanFactory();
	}
跟进refreshBeanFactory()方法,关键地方都已经加上注释,也比较简单易懂,跟着看一下就行。
	/**
	 * This implementation performs an actual refresh of this context's underlying
	 * bean factory, shutting down the previous bean factory (if any) and
	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 * 此实现执行此上下文的底层 bean 工厂的实际刷新,
	 * 关闭先前的 bean 工厂(如果有)并为上下文生命周期的下一阶段初始化一个新的 bean 工厂。
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		// 如果有前一个,先关闭
		if (hasBeanFactory()) {
			// 先销毁所有已经注册的bean
			destroyBeans();
			// 关闭工厂,将this.beanFactory设为null
			closeBeanFactory();
		}
		try {
			//创建DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			//为了序列化指定id,如果需要的话,让这个beanFactory从id反序列化到BeanFactory对象
			beanFactory.setSerializationId(getId());
			//定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象已经循环依赖
			//以及设置@Autowire和@Qualifier注册解析器QualifierAnnotationAutowire-CandidateResolver
			customizeBeanFactory(beanFactory);
			//初始化DocumentReader,并进行xml文件读取和解析
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
前面的准备工作是比较简单的,不再细说。重点在loadBeanDefinitions(beanFactory)方法,这里会加载xml获取我们配置的BeanDefinition。
记住这个位置AbstractRefreshableApplicationContext#refreshBeanFactory(),这是经过前面繁杂的摸索后真正进入BeanDefinition加载的分水岭。

跟进代码查看。
	/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * 通过 XmlBeanDefinitionReader 加载 bean 定义。
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		//为指定 beanFactory创建XmlBeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
		// Configure the bean definition reader with this context's
		// resource loading environment.
		//对 beanDefinitionReader 进行环境变量的设置
		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.
		// 允许子类提供阅读器的自定义初始化,然后继续实际加载 bean 定义。
		// 对 beanDefinitionReader 进行属性设置
		initBeanDefinitionReader(beanDefinitionReader);
		// 加载 bean 定义
		loadBeanDefinitions(beanDefinitionReader);
	}
可以看到这里也是进行了一波准备工作,首先是给beanFactory创建一个XmlBeanDefinitionReader,后续xml解析都是由它来完成,接下来对这个XmlBeanDefinitionReader进行一些属性设置,接下来调用loadBeanDefinitions(beanDefinitionReader)加载BeanDefinition,跟进代码查看。
	/**
	 *
	 * 使用给定的 XmlBeanDefinitionReader 加载 bean 定义。
	 * <p>bean 工厂的生命周期由 {@link refreshBeanFactory} 方法处理;因此这个方法只是应该加载和或注册 bean 定义。
	 *
	 * 在初始化了DefaultListableBeanFactory和XmlBeanDefinitionReader后就可以进行配置文件的读取了
	 *
	 */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		// 返回一个引用构建此上下文的 XML bean 定义文件的 Resource 对象数组, 默认实现返回 {@code null}。
		// 子类可以覆盖它以提供预构建的资源对象而不是占位符字符串。
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		// 返回引用构建此上下文的 XML bean 定义文件资源位置数组,还可以包括占位符模式,这将通过 ResourcePatternResolver 进行解析获取。
		// <p>默认实现返回 {@code null}。子类可以覆盖它以提供一组资源位置以从中加载 bean 定义。
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			reader.loadBeanDefinitions(configLocations);
		}
	}
可以看到,由于没有预构建的资源,所以configResources会为null,configLocations则获取到了我们的beans.xml,那么接下来就会解析beans.xml获取BeanDefinition。

跟进reader.loadBeanDefinitions(configLocations)方法,具体的实现是在 AbstractBeanDefinitionReader#loadBeanDefinitions(String... locations)方法。
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}
继续套娃,进入loadBeanDefinitions(location)方法。
	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		//获取资源加载器,主要功能是根据路径和类加载器获取Resource对象
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}
		//ResourcePatternResolver用于加载多个文件或者加载Ant风格路径的资源文件
		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				// 以Resource形式返回所有配置文件
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				// 通过resources加载
				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 {
			/**
			 * 加载单个资源,直接使用ResourceLoader加载
			 */
			// 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;
		}
	}
这里会经过一系列的重载方法,最终会来到doLoadBeanDefinitions()方法里,这才是真正解析的地方,好家伙,山路十八弯。

跟进doLoadBeanDefinitions()方法。
	/**
	 * 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 {
			/**
			 * 创建Document对象,XML文档对象,即dom树
			 * 使用这个Document对象可以获取XML文件中的节点并且创建节点
			 * SAX XML
			 * 解析dom树,即解析出一个个属性,将其属性保存到BeanDefinition当中
			 * 并向容器注册BeanDefinition
			 */
			Document doc = doLoadDocument(inputSource, resource);
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		// 省略部分异常信息
	}
获取Document对象就不说了,可以单独出一篇文章Spring Ioc源码分析系列--获取xml文件Document对象,这里再跟进去就离主题太远了。
跟进registerBeanDefinitions(doc, resource)方法。
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//创建BeanDefinitionDocumentReader,这个是实际从XML的dom树中服务BeanDefinition
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//获取注册表BeanDefinitionMap在本次加载前的BeanDefinition数量
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加载并注册
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//用本次加载后的数据减去以前有的数量,即为本次加载的BeanDefinition数量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
重点在方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))里,跟进代码。
	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.
		//BeanDefinition的解析委托类
		BeanDefinitionParserDelegate parent = this.delegate;
		//判断这个根节点是否是默认的命名空间
		//底层就是判断这个根节点的namespace="http://www.springframework.org/schema/beans"
		this.delegate = createDelegate(getReaderContext(), root, parent);
		if (this.delegate.isDefaultNamespace(root)) {
			//获取这个profile的值,表示剖面,用于设置环境
			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;
				}
			}
		}
		//解析XML之前做的准备工作,其实什么也没做
		preProcessXml(root);
		//调用这个方法解析
		parseBeanDefinitions(root, this.delegate);
		//后续处理
		postProcessXml(root);
		this.delegate = parent;
	}
跟进解析BeanDefinition的方法parseBeanDefinitions(root, this.delegate)里面。
	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;
					//Bean定义的document对象使用了spring的默认命名空间,如http://www.springframework.org/schema/beans
					if (delegate.isDefaultNamespace(ele)) {
						//若是则按照spring原有的逻辑进行解析
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//使用扩展的自定义代理类去解析
			delegate.parseCustomElement(root);
		}
	}
这个例子没有自定义标签,进入到默认标签的解析。
	//根据不同的标签进行解析
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//如果是import标签,进入导入解析
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//若果是别名元素,则进行别名解析
		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);
		}
	}
显然我们的例子只有一个Bean标签,所以会进入到processBeanDefinition()方法里。
	/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//BeanDefinitionHolder是对BeanDefinition的封装,即bean定义的封装类
		//对document对象中的bean标签解析由BeanDefinitionParserDelegate实现
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				//从springIOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
				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));
		}
	}
调用BeanDefinitionReaderUtils.registerBeanDefinition()方法真正将BeanDefinition注册进容器里。咋注册呢?其实就是加到BeanFactory的beanDefinitionMap属性里。beanDefinitionMap可以说就是BeanDefinition的容器了。
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		// 这里真正把BeanDefinition注册到了BeanFactory里
		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);
			}
		}
	}
到这里已经完成了BeanDefinition的注册,是不是有点曲折?其实很简单,就是细节比较多。
到这里obtainFreshBeanFactory()方法已经基本结束,已经完成了配置文件的解析并且注册BeanDefinition的过程了。剩下的操作就是把这个BeanFactory返回给上一步。
prepareBeanFactory()方法
接下来分析一下prepareBeanFactory()方法,这方法也简单,主要就是对BeanFactory进行一些属性的设置,跟着注释看一下就行。
	/**
	 * Configure the factory's standard context characteristics,
	 * such as the context's ClassLoader and post-processors.
	 * @param beanFactory the BeanFactory to configure
	 */
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
		// 设置 beanFactory的classLoader 为当前 context的classLoader
		beanFactory.setBeanClassLoader(getClassLoader());
		/**
		 * 设置 beanFactory的表达式语言处理器,Spring3 增加了表达式语言的支持,
		 * 默认可以使用#{bean.xxx}的形式来调用相关属性值
		 * @Qusetion 在注册了这个解析器之后,spring是在什么时候调用这个解析器的呢?
		 * Spring在bean进行初始化的时候会有属性填充的步骤,而在这一步中
		 * Spring会调用AbstractAutowireCapableBeanFactory类的applyPropertyValues函数来完成功能。
		 * 就这个函数中,会通过构造 BeanDefinitionValueResolver类型实例valueResolver来进行属性值的解析。
		 * 同时,也是在这个步骤中一般通过 AbstractBeanFactory 中的 evaluateBeanDefinitionString
		 * 方法去完成SpEL解析
		 */
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
		//为beanFactory增加一个PropertyEditor,这个主要是对bean的属性等设置管理的一个工具
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
		// Configure the bean factory with context callbacks.
		//添加beanPostProcessor
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
		//设置几个忽略自动装配的接口
		// aware都是由invokeAware方法注入,忽略自动Autowire
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		// 设置了几个自动装配的规则,后面三个都是把当前对象注册了进去,只有BeanFactory老老实实得注册了一个BeanFactory
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);
		// Register early post-processor for detecting inner beans as ApplicationListeners.
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
		// Detect a LoadTimeWeaver and prepare for weaving, if found.
		// 添加对AspectJ的支持
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
		// Register default environment beans.添加系统环境默认的bean
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}
到这里refresh()的前面三个方法已经简单过完了,除了解析配置文件复杂点,其他的都是些属性配置居多。
小结
本文先通过一个列子驱动,找到了Ioc容器启动的入口,简单分析了一下前三个方法,最重要的就是加载BeanDefinition的过程了,可以仔细看多几遍。
今天发生了点小插曲,本该写得更详细点的,但是写不了了,只能作罢,将就着看吧。
如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。
Spring Ioc源码分析系列--Ioc源码入口分析的更多相关文章
- YARN分析系列之三 -- 从脚本入口分析 ResourceManager的初始化过程
		1. 由脚本找到 RM 主类 这部分,我们从脚本作为入口去逐步深入ResourceManager源码. 从 Hadoop 官方文档 中可以看到 ResourceManager 的启动命令为: Usag ... 
- Spring Ioc源码分析系列--Ioc的基础知识准备
		Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ... 
- Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析
		Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ... 
- Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理
		Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那么这一篇继续 ... 
- JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)
		文章系作者原创,如有转载请注明出处,如有雷同,那就雷同吧~(who care!) 一.写在前面 这是源码分析计划的第一篇,博主准备把一些常用的集合源码过一遍,比如:ArrayList.HashMap及 ... 
- 【原】Spring源码浅析系列-导入源码到Eclipse
		用了Spring几年,平时也断断续续在项目里看过一些源码,大多都是比较模糊的,因为一旦从一个地方进去就找不到方向了,只能知道它大概是做了什么事能达到这个功能或者效果,至于细节一般没有太深入去研究.后来 ... 
- Thinkphp源码分析系列(一)–入口文件
		正如官方文档上所介绍的,thinkphp使用单一入口,所有的请求都从默认的index.php文件进入.当然不是说一定非得从index.php进入,这应该取决于你的服务器配置,一般服务器都会有默认的首页 ... 
- Spring Ioc源码分析系列--Bean实例化过程(一)
		Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ... 
- 【java集合框架源码剖析系列】java源码剖析之HashSet
		注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ... 
随机推荐
- ACL 权限控制机制 ?
			UGO(User/Group/Others) 目前在 Linux/Unix 文件系统中使用,也是使用最广泛的权限控制方式.是一种粗 粒度的文件系统权限控制模式. ACL(Access Control ... 
- Spring的@Enable*注解的工作原理
			转自:https://blog.csdn.net/chengqiuming/article/details/81586948 一 列举几个@Enable*注解的功能 @EnableAspectJAut ... 
- Spark学习摘记 —— RDD行动操作API归纳
			本文参考 参考<Spark快速大数据分析>动物书中的第三章"RDD编程",前一篇文章已经概述了转化操作相关的API,本文再介绍行动操作API 和转化操作API不同的是, ... 
- Docker镜像构建之docker commit
			我们可以通过公共仓库拉取镜像使用,但是,有些时候公共仓库拉取的镜像并不符合我们的需求.尽管已经从繁琐的部署工作中解放出来了,但是在实际开发时,我们可能希望镜像包含整个项目的完整环境,在其他机器上拉取打 ... 
- Linux编程 | 使用 make
			目录 简单的 makefile 文件 常规的 makefile 文件 常用参数 make 内置规则 后缀和模式规则 make 管理函数库 在Linux 环境中,make 是一个非常重要的编译命令.不管 ... 
- int 越界处理
			在程序运行中,如果运算结果太大超出了所定义的类型,那么可能会影响整个程序的运行结果,甚至是程序出错. 因此,在编写程序时,加入越界处理就显得一起重要了. 用整型 int 来举个例子吧,int型占四个字 ... 
- SVG中的坐标系统和坐标变换
			视野和世界 2D绘图中很多人会有一个误区,就是我绘图的区域是一个矩形区域.无论新建一个画布还是创建了一个容器,心里都想象里面有一个矩形区域.其实,在SVG当中,矩形区域只是视野,是我们看到的部分.实际 ... 
- tomcat启动报错:A child container failed during start
			环境:maven3.3.9+jdk1.8+tomcat8.5 错误详细描述: 严重: A child container failed during start java.util.concurren ... 
- java中什么叫覆盖Override?请给实例
			5.覆盖(Override) 马克-to-win:方法的覆盖(Override)是指子类重写从父类继承来的一个同名方法(参数.返回值也同). 例1.5.1-- class AAAMark_to_win ... 
- openlayers离线瓦片地图开发
			近期业务繁忙...待更新 
