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,这里的逻辑很简单,就是根据传入的nameage返回一个新的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我需要一个UserServicebean

<?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:xsixsi:schemaLocation是指明了xml文件的验证模型和验证模型文件的位置。可以看到验证模型能通过http方式获取,但是为了防止网络抖动的影响,一般会在本地存放验证文件。spring-beans.xsd就可以在图示的目录下找到。

接下来就到了<bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/>这一行,这是我们获取一个Bean的关键配置,这里告诉Ioc我需要一个iduserServiceclassio.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会为nullconfigLocations则获取到了我们的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注册进容器里。咋注册呢?其实就是加到BeanFactorybeanDefinitionMap属性里。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源码入口分析的更多相关文章

  1. YARN分析系列之三 -- 从脚本入口分析 ResourceManager的初始化过程

    1. 由脚本找到 RM 主类 这部分,我们从脚本作为入口去逐步深入ResourceManager源码. 从 Hadoop 官方文档 中可以看到 ResourceManager 的启动命令为: Usag ...

  2. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

  3. Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析

    Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...

  4. Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理

    Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那么这一篇继续 ...

  5. JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)

    文章系作者原创,如有转载请注明出处,如有雷同,那就雷同吧~(who care!) 一.写在前面 这是源码分析计划的第一篇,博主准备把一些常用的集合源码过一遍,比如:ArrayList.HashMap及 ...

  6. 【原】Spring源码浅析系列-导入源码到Eclipse

    用了Spring几年,平时也断断续续在项目里看过一些源码,大多都是比较模糊的,因为一旦从一个地方进去就找不到方向了,只能知道它大概是做了什么事能达到这个功能或者效果,至于细节一般没有太深入去研究.后来 ...

  7. Thinkphp源码分析系列(一)–入口文件

    正如官方文档上所介绍的,thinkphp使用单一入口,所有的请求都从默认的index.php文件进入.当然不是说一定非得从index.php进入,这应该取决于你的服务器配置,一般服务器都会有默认的首页 ...

  8. Spring Ioc源码分析系列--Bean实例化过程(一)

    Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...

  9. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

随机推荐

  1. ACL 权限控制机制 ?

    UGO(User/Group/Others) 目前在 Linux/Unix 文件系统中使用,也是使用最广泛的权限控制方式.是一种粗 粒度的文件系统权限控制模式. ACL(Access Control ...

  2. Spring的@Enable*注解的工作原理

    转自:https://blog.csdn.net/chengqiuming/article/details/81586948 一 列举几个@Enable*注解的功能 @EnableAspectJAut ...

  3. Spark学习摘记 —— RDD行动操作API归纳

    本文参考 参考<Spark快速大数据分析>动物书中的第三章"RDD编程",前一篇文章已经概述了转化操作相关的API,本文再介绍行动操作API 和转化操作API不同的是, ...

  4. Docker镜像构建之docker commit

    我们可以通过公共仓库拉取镜像使用,但是,有些时候公共仓库拉取的镜像并不符合我们的需求.尽管已经从繁琐的部署工作中解放出来了,但是在实际开发时,我们可能希望镜像包含整个项目的完整环境,在其他机器上拉取打 ...

  5. Linux编程 | 使用 make

    目录 简单的 makefile 文件 常规的 makefile 文件 常用参数 make 内置规则 后缀和模式规则 make 管理函数库 在Linux 环境中,make 是一个非常重要的编译命令.不管 ...

  6. int 越界处理

    在程序运行中,如果运算结果太大超出了所定义的类型,那么可能会影响整个程序的运行结果,甚至是程序出错. 因此,在编写程序时,加入越界处理就显得一起重要了. 用整型 int 来举个例子吧,int型占四个字 ...

  7. SVG中的坐标系统和坐标变换

    视野和世界 2D绘图中很多人会有一个误区,就是我绘图的区域是一个矩形区域.无论新建一个画布还是创建了一个容器,心里都想象里面有一个矩形区域.其实,在SVG当中,矩形区域只是视野,是我们看到的部分.实际 ...

  8. tomcat启动报错:A child container failed during start

    环境:maven3.3.9+jdk1.8+tomcat8.5 错误详细描述: 严重: A child container failed during start java.util.concurren ...

  9. java中什么叫覆盖Override?请给实例

    5.覆盖(Override) 马克-to-win:方法的覆盖(Override)是指子类重写从父类继承来的一个同名方法(参数.返回值也同). 例1.5.1-- class AAAMark_to_win ...

  10. openlayers离线瓦片地图开发

    近期业务繁忙...待更新