目录
一、总览
经过前一篇(Spring源码学习【二】IOC容器的初始化(一)Resource定位)的分析,我们对IOC容器的初始化过程已经有了一定的了解。IOC的初始化由refresh()方法启动,最终对Resource的载入是由XmlBeanDefinitionReader处理的。参考上一节中6.loadBeanDefinitions的分析,AbstractBeanDefinitionReader中多处调用了loadBeanDefinitions(Resource)方法,这是一个模板方法,实际由其子类XmlBeanDefinitionReader实现,并完成BeanDefinition载入的过程。
BeanDefinition的载入可以分为两个过程:首先,通过documentLoader获取得到Document对象;然后,根据Spring Bean的规则解析Document得到BeanDefinition,下面从源码的角度对这个过程进行分析。
二、源码分析
(一)获取Document
首先, XmlBeanDefinitionReader中重写了父类的loadBeanDefinitions(Resource)方法,这个方法就是加载BeanDefinition的入口,在这个方法中,将Resource封装成带有编码信息的EncodedResource对象。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
……
/**
* 根据指定的xml资源文件加载BeanDefinition
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 这里会为Resource添加编码等信息封装成一个EncodedResource对象
return loadBeanDefinitions(new EncodedResource(resource));
}
}
然后,在loadBeanDefinitions(EncodedResource)中处理了Resource的循环加载,并将Resource封装成代表XML实体的InputSource(这里对LocalThread和循环加载存在疑问)。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
……
/**
* 根据指定的xml资源文件加载BeanDefinition,允许指定解析文件使用的编码方式
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreEx-ception {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
// private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
// new NamedThreadLocal<>("XML bean definition resources currently being loaded");
// 这里使用的localThread会为每个线程创建一个Set<EncodedResource>副本,保证线程之间的数据访问不会出现冲突
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 我们在配置文件中可以通过<import>标签引用其他资源文件,当出现资源文件之间的循环引用时会抛出异常
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// Resource是封装了一系列I/O操作的资源,这里拿到输入流InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 将InputStream封装成一个InputSource,InputSource是xml SAX解析的数据源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 开始加载BeanDefinition
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();
}
}
}
}
接下来,调用了 doLoadBeanDefinitions(InputSource, Resource)方法,真正开始从XML资源文件加载BeanDefinition。前面已经提到过,BeanDefinition的载入首先需要获取Document,这里有两个核心方法:其一,doLoadDocument;其二,registerBeanDefinitions。
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
……
/**
* 真正开始从XML资源文件加载BeanDefinition
* 两个核心方法:doLoadDocument和registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
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);
}
}
/**
* 通过DocumentLoader加载XML资源文件,以Document的形式返回,用于后续的XML解析。
* 这里的Document是由w3c定义的一个接口,用于表示整个html或xml文件。
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// inputSource:SAX解析方式的输入数据
// getEntityResolver:实体解析器,分为ResourceEntityResolver和DelegatingEntityResolver
// ResourceEntityResolver通常是对applicationContext的包装
// DelegatingEntityResolver是对dtdResolver和schemaResolver的包装,分别对应关于DTDs和SCHEMAs请参考http://wiki.jikexueyuan.com/project/xml/dtds.html
// errorHandler SAX解析的错误错误处理
// getValidationModeForResource:获取xml文件的验证方式(dtd或xsd方式)
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
}
/**
* 根据给定的Resource资源获取XML验证方式
* 如果没有明确指定验证方式,则会根据Resource内容进行判断
*/
protected int getValidationModeForResource(Resource resource) {
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 读取Resource内容判断验证规则
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
return VALIDATION_XSD;
}
/**
* 检测在XML文件上执行的哪种验证
* 如果内容包含DOCTYPE则为DTD验证,否则为XSD验证
*/
protected int detectValidationMode(Resource resource) {
if (resource.isOpen()) {
throw new BeanDefinitionStoreException("Passed-in Resource [" + resource + "] contains an open stream: " + "cannot determine validation mode automatically. Either pass in a Resource " + "that is able to create fresh streams, or explicitly specify the validationMode " + "on your XmlBeanDef-initionReader instance.");
}
InputStream inputStream;
try {
inputStream = resource.getInputStream();
}
catch (IOException ex) {
throw new BeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + "Did you attempt to load directly from a SAX InputSource without specifying the " + "validationMode on your XmlBeanDefinitionReader instance?", ex);
}
try {
// 进行检测
return this.validationModeDetector.detectValidationMode(inputStream);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex);
}
}
/**
* 注册DOM document中包含的BeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefini-tionStoreException {
// 首先创建一个读取器,这里使用的是读取Spring默认XML格式(DTD和XSD格式)BeanDefinition的读取器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 注册BeanDefinition,是一个模板方法,实际调用的是DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
}
到这里,已经完成了BeanDefinition载入的第一个过程:获取Document对象,用这个对象来表示XML资源文件,下面一步重要的操作就是对Document进行解析了。
(二)解析Document
经过以上的分析,我们已经知道,解析Document是在DefaultBeanDefinitionDocumentReader中完成的,下面让我们来看一看DefaultBeanDefinitionDocumentReader的部分代码:
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader{
……
/**
* 实现根据Spring bean规则解析BeanDefinition的功能
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获取到XML的根元素:<beans/>
Element root = doc.getDocumentElement();
// 解析根元素下的BeanDefinition
doRegisterBeanDefinitions(root);
}
/**
* 实现根据Spring bean规则解析BeanDefinition的功能
*/
protected void doRegisterBeanDefinitions(Element root) {
// <beans/> 嵌套时会递归调用该方法,首先记录下当前的委托对象为父对象,然后创建一个新的子委托对象,在本次调用中使用子对象进行处理,目的是为了初始化每个<beans>的default-*属性,比如 <beans de-fault-lazy-init="true" > 等
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
// profile环境处理
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDele-gate.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和postProcessXml都是模板方法,默认实现是空的,留给子类进行扩展,子类中可以处理一些自定义的bean,并把这些bean转换为标准的Spring BeanDefinition
preProcessXml(root);
// 解析<beans/>下的BeanDefinition
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
/**
* 解析root元素下的 import、alias、bean
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 检查root元素是否为http://www.springframework.org/schema/beans默认命名空间
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;
// 处理beans默认命名空间下的元素标签,<import>、<alias>、<bean>、<beans>
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
// 处理非默认命名空间元素标签,如<context:component-scan>、<tx:annotation-driven>等
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate){
// 解析<import>标签,从引用的资源中加载BeanDefinition
// 这里的解析可能会抛出资源循环加载的异常
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析<alias>,向bean工厂注册别名
// 具体是在SimpleAliasRegistry这个类中,以HaspMap持有bean的别名和原名的关系的
// 这里需要注意别名的循环引用
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析<bean>,并向bean工厂注册bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 这里体现了<beans>嵌套的递归
doRegisterBeanDefinitions(ele);
}
}
/**
* 解析<bean>并向bean工厂注册
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinitionParserDelegate解析bean元素,这里的BeanDefinitionHolder是对BeanDefinition、BeanName、别名的封装,BeanDefinition中包含了id、class、name等属性。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 解析元素下的自定义标签
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向bean 工厂注册BeanDefinition和bean别名
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderCon-text().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
}
到这里,我们发现真正将Document元素解析为BeanDefinition的过程是委托给BeanDefinitionParserDelegate处理的,在这个类中我们需要关注两个重点:其一,解析bean元素;其二,解析自定义元素。代码如下:
public class BeanDefinitionParserDelegate {
……
/**
* 解析<bean>元素
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* 解析<bean>元素
*/
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 获取元素的id和name属性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 以id或别名作为bean的名称
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
}
// 检查bean名称和别名是否已经被注册
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 解析element元素,得到具体的BeanDefinition
// 这里不会处理beanName和aliases
// TODO:待深入学习
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, con-tainingBean);
// 如果beanDefinition没有指定id和name,则为其生成beanName
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 最后,返回一个BeanDefinitionHolder对象,这个对象中持有beanDefinition、beanName、beanAliases
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
/**
* 解析自定义元素
*/
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
/**
* 解析自定义元素
*/
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 获取元素的命名空间
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 获取相应命名空间的Handler,各个命名空间都有对应的Handler,如ContextNamespaceHandler、AopNamespaceHandler等
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));
}
}
到这里,Document的解析就完成了,至于BeanDefinition具体的解析处理这里没有深入学习,后续会补上。