目录

Spring源码 - 程序入口和XML解析

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


jack大佬 的话来概括,为什么要学习Spring源码?

  • 提高自己的写代码能力
  • 从全局考虑如何使代码变得灵活可扩展
  • Spring重新定义了Java
  • Spring源码是有一定难度的
  • 如果想成为真正的高手,Spring源码必须攻克,不是危言耸听的
  • Spring源码读懂,其他很多基于Spring的框架源码很容易理解。相反,以Spring为基础的框架、组件,不懂Spring压根没法读其源码。

学习源码的方式:主抓脉络,后看细节。(熟练使用的前提下再看源码

特别感谢:Spring源码中的调用链路、纵深都是比较长的,我个人读了很久。许多源码难点的理解得益于 jack大佬 的讲解,感谢大佬的授业解惑。

1 Spring的历史

  1. 2002年10月,Rod Johnson发布《Expert One-on-One J2EE设计和开发》一书
  2. 2004年3月,Spring1.0发布
  3. 2006年10 月,Spring2.0发布
  4. 2009年12月,Spring3.0发布
  5. 2013年12月,发布Spring4.0
  6. 2017年9月,Spring5.0发布

2 读源码前的准备

  1. JDK1.8版本
  2. spring 5.1.3.RELEASE版本
  3. 补一下Lambda表达式的知识
  4. 安装配置gradle

3 Spring源码下载

  1. git clone --branch v5.1.3.RELEASE https://gitee.com/wanglizhi00/spring-framework (此链接的Spring源码,核心流程、要注意的点我都做了中文注释,非常详细。如果你的网络足够好,也可以到 github 下载 原版spring源码
  2. gradle下载,gradle要JDK8的版本
  3. 到下载的spring源码路径执行gradle命令,gradlew :spring-oxm:compileTestJava
  4. 用idea打开spring源码工程。(在idea中安装插件kotlin,重启idea,跟IDEA版本有关较新版本不用安装
  5. 把编译好的源码导入到工程中
  6. spring工程如何搭建这里不做介绍,如何将down下来的源码导入到自己的spring工程依赖库, 我的Gitee-Spring源码注释 中有详细的介绍

4 入口方法

容器:AbstractApplicationContext 抽象父类,核心方法 refresh()。

  • ClassPathXmlApplicationContext XML方式启动。
  • AnnotationConfigWebApplicationContext 注解方式启动,对局部代码进行测试时比较好用。
  • AnnotationConfigServletWebServerApplicationContext SpringBoot启动默认使用的上下文类。

我们知道,spring容器的启动是通过构造ApplicationContext的一个实例开始的。以ClassPathXmlApplicationContext为例。

https://oss.wlizhi.cc/blog/spring/ClassPathXmlApplicationContext.png
ClassPathXmlApplicationContext UML图

5 Xml解析流程概览

首先进入spring容器启动的核心方法:refresh() ,创建BeanFactory对象 obtainFreshBeanFactory()

  1. 创建XmlBeanDefinitionReader对象
  2. 通过Reader对象加载配置文件
  3. 根据加载的配置文件把配置文件封装成document对象
  4. 创建BeanDefinitionDocumentReader对象,DocumentReader负责对document对象解析
  5. parseDefaultElement(ele, delegate);负责常规标签解析
  6. delegate.parseCustomElement(ele);负责自定义标签解析
  7. 最终解析的标签封装成BeanDefinition并缓存到容器中

6 创建XmlBeanDefinitionReader对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		// 创建BeanDefinitionReader
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		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.
		initBeanDefinitionReader(beanDefinitionReader);
		// TODO 关键点:加载BeanDefinition
		loadBeanDefinitions(beanDefinitionReader);
	}
}

在loadBeanDefinitions方法的第一行,创建了XmlBeanDefinitionReader对象。这个类的主要作用是读取BeanDefinition,并将XML文档的读取委托给BeanDefinitionDocumentReader。

7 创建BeanDefinitionDocumentReader对象

来到loadBeanDefinitions(beanDefinitionReader);这里将要读取的xml路径传递进来,循环xml路径,依次进行解析。
注:看注释中的关键点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
    // 首先进入这个方法
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		// 一次解析配置文件,加载配置文件中定义的Bean。
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}
}

路径最终会被封装为InputSource对象,使用委托模式,交给BeanDefinitionDocumentReader处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    // 第一步:入口
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			// 解析xml配置文件,获取到Document对象
			Document doc = doLoadDocument(inputSource, resource);
			// 解析、注册BeanDefinition
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
        //此处分别捕获了很多种异常,省略了。
		} catch (Exception ex) {
          			throw ex;
		}
    }
    // 第二步 创建BeanDefinitionDocumentReader,委托给BeanDefinitionDocumentReader进行解析工作。
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		// 委托模式,将Document的解析及BeanDefinition的注册交给BeanDefinitionDocumentReader
		// 这里使用的DefaultBeanDefinitionDocumentReader进行解析
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		// TODO 关键点:解析xml、注册BeanDefinition
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
}

8 标签解析入口

在解析标签之前、和之后,分别有一个钩子方法。这两个钩子方法分别是 解析xml并注册BeanDefinition 之前和之后的扩展方法,ClassPathXmlApplicationContext中,这两个方法体都是空的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    // 第一步
    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;
		// BeanDefinitionParserDelegate中封装了预定义的很多标签属性
		this.delegate = createDelegate(getReaderContext(), root, parent);

		// 根标签解析 NameSpace:http://www.springframework.org/schema/beans
		if (this.delegate.isDefaultNamespace(root)) {
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}
		// 钩子方法,解析xml注册BeanDefinition之前调用,可以不看
		preProcessXml(root);
		// TODO 解析xml,注册BeanDefinition
		parseBeanDefinitions(root, this.delegate);
		// 钩子方法,解析xml注册BeanDefinition之后调用,可以不看
		postProcessXml(root);

		this.delegate = parent;
	}
    // 第二步
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// Spring根标签,NameSpace:http://www.springframework.org/schema/beans
		// 这里循环document中的元素,进行解析默认标签及自定义标签
		String tagName = root.getTagName();
		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)) {
						logger.debug("delegate.isDefaultNamespace(root):true,解析默认标签 " + tagName);
						// TODO 关键点:解析默认标签
						parseDefaultElement(ele, delegate);
					} else {
						logger.debug("delegate.isDefaultNamespace(root):true,解析自定义标签 " + tagName);
						// TODO 关键点:解析自定义标签
						delegate.parseCustomElement(ele);
					}
				}
			}
		} else {
			logger.debug("delegate.isDefaultNamespace(root):false,解析自定义标签 " + tagName);
			// TODO 关键点:解析自定义标签
			delegate.parseCustomElement(root);
		}
	}
}

9 解析默认标签

  1. 根据xml节点的名称,对比代码中定义的标签名称。跳转到对应的解析方法
  2. 以bean标签为例,最终走到BeanDefinitionParserDelegate.parseBeanDefinitionElement()方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    // 第一步 进入标签解析方法
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// import标签解析
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// alias标签解析
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// TODO 重点看:bean标签解析
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		// beans标签解析
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
    // 第二步 进入bean标签解析方法
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		// TODO 重点:获取到BeanDefinitionHolder,内部封装了BeanDefinition
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			// 如果必要,装饰BeanDefinition,这里用到了装饰模式
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
				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));
		}
	}
}

解析和封装BeanDefinition,这里解析了bean标签中的所有属性,将这些属性值封装成BeanDefinition对象。最终会被注册到BeanDefinitionRegistry中。
BeanDefinitionRegistry作为spring容器中BeanFactory的全局成员变量。也就是说,最终得到的BeanDefinition贯穿整个spring容器的生命周期,它是对spring容器中所有将要实例化的bean信息的描述。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class BeanDefinitionParserDelegate {
    @Nullable
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, @Nullable BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));

		// bean标签中的class属性
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}
		// bean标签中的parent属性
		String parent = null;
		if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
			parent = ele.getAttribute(PARENT_ATTRIBUTE);
		}

		try {
			// TODO 重点:创建BeanDefinition,设置parent、beanClass、beanClassName属性。
			// bean标签的bean均定义为:GenericBeanDefinition
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			// TODO 重点:解析element并设置BeanDefinition中的属性值
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			// 解析meta标签、lookupOverride子标签、replaceMethod子标签
			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			// 解析构造函数参数、property参数、qualifier参数
			parseConstructorArgElements(ele, bd);
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);

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

			return bd;
		}
		catch (ClassNotFoundException ex) {
			error("Bean class [" + className + "] not found", ele, ex);
		}
		catch (NoClassDefFoundError err) {
			error("Class that bean class [" + className + "] depends on not found", ele, err);
		}
		catch (Throwable ex) {
			error("Unexpected failure during bean definition parsing", ele, ex);
		}
		finally {
			this.parseState.pop();
		}

		return null;
	}
}

10 解析自定义标签

SPI方式获取加载所有的namespaceUri(SPI这里不进行讨论),通过当前标签定义的namespaceUri,获取到初始化后(init())的处理类。然后调用处理类的parse方法,在此方法中,完成了标签属性的解析和BeanDefinition注册

在resolve方法中,调用了NamespaceHandler的init方法,init方法中注册了标签属性的处理类。

这些处理类都是BeanDefinitionParser的实现,在BeanDefinitionParser中,仅有一个parse方法,用来解析特定的标签属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class BeanDefinitionParserDelegate {
	@Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // SPI方式获取namespaceUri
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 获取NamespaceHandler,这里面调用了NamespaceHandler的init方法
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}

NamespaceHandler,是根据对应模块中的META-INF/spring.handlers中定义的映射关系进行获取的(SPI方式获取)。映射关系NamespaceUri=NamespaceHandler

以下是spring-context中定义的处理映射(spring-context/META-INF/spring.handlers

1
2
3
4
5
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler

resolve方法获取了处理类对象,每个处理类实例化之后都会放入缓存handlerMappings,下次获取直接从缓存中获取。handlerMappings的键时NamespaceUri,值初始是对应处理类的ClassName,实例化之后会替换为该类的实例对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
    @Override
	@Nullable
	public NamespaceHandler resolve(String namespaceUri) {
		// 从缓存中获取,如果存在,直接返回,如果不存在则创建
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				// 加载Class
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				// TODO 重点:实例化NamespaceHandler,调用init方法,放入缓存。这里是一个扩展点,所有的自定义标签都要提供对应的NamespaceHandler
				//  spring.handler文件中定义了很多内置的NamespaceHandler,通过SPI的方式加载、实例化
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				namespaceHandler.init();
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
						"] for namespace [" + namespaceUri + "]", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
						className + "] for namespace [" + namespaceUri + "]", err);
			}
		}
	}
}

每个自定义标签都有自己的NamespaceHandler实现。

https://oss.wlizhi.cc/blog/spring/NameSpaceHandlerImpls.png
NamespaceHandler实现类

context标签为例,注册不同标签属性对应的处理类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class ContextNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		// 注册各种标签属性的解析类
		registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
		registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
		registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
		registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
		registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
		registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
	}

}

上面代码registerBeanDefinitionParser方法,会将(标签属性名称-解析类的实例)缓存到NamespaceHandlerSupport.parsers变量中(这是一个map集合)。

1
2
3
4
5
6
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
    // 将(标签属性名称-解析类的实例)映射关系缓存起来,记着这个成员变量,后面就是从这个变量中获取解析类的
    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
		this.parsers.put(elementName, parser);
	}
}

回到BeanDefinitionParserDelegate.parseCustomElement方法,最后一行handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)),调用了对应的处理类的parse方法(前面有提到的一个parsers成员变量,标签属性名-处理类实例映射

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
    // 第一步
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 从parsers缓存中获取对应的BeanDefinitionParser处理类,调用parse方法
		BeanDefinitionParser parser = findParserForElement(element, parserContext);
		return (parser != null ? parser.parse(element, parserContext) : null);
	}
    // 第二步
	@Nullable
	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        // parsers这个变量很重要,里面预先缓存了标签属性名称-处理类实例的映射关系,这个方法作用
		// 就是根据标签属性名获取从parsers中获取处理类
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}
}

11 总结

Spring解析XML,绕了这么大一圈。其目的就是把XML中配置的各种标签按照标签本身定义的解析方式进行处理,将处理的结果封装成BeanDefinition对象,并注入到BeanFactory的成员变量BeanDefinitionRegistry中。

这是Spring容器以XML方式初始化的第一步。其实无论是XML解析方式,还是注解方式,基本思想都是一样的。Spring容器在创建任何实例前,都会先搜集这个将要创建实例的一些必要信息,然后对这些信息根据一些扩展接口中提供的方法(模板设计模式),进行处理。

这些处理有一些是Spring内置的,可能也有使用者自定义的。具体的处理方式,在后面的章节体现。