上一节介绍了资源的定位,既然资源已经定位了,本章将进入具体的解析工作,具体的解析工作是从XmlBeanDefinitionReader类的loadBeanDefinitions(Resource resource)方法开始。
一. loadBeanDefinitions(Resource resource)源码如下 XmlBeanDefinitionReader类
public int loadBeanDefinitions(Resource resource)
throws BeanDefinitionStoreException
{
return loadBeanDefinitions(new EncodedResource(resource));
}
EncodedResource是对Resource的封装,采用装饰模式,主要是增加了编码的信息.
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set currentResources = (Set) this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected recursive loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.set(null);
}
}
}
这个方法里最核心的操作是doLoadBeanDefinitions(inputSource, encodedResource.getResource())
/**
* 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
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
}
DocumentLoader类主要负责把一个XML文档解析成一个Document对象,注册beanDefinitions的工作交给registerBeanDefinitions方法了.
/**
* Register the bean definitions contained in the given DOM document.
* Called by <code>loadBeanDefinitions</code>.
* <p>Creates a new instance of the parser class and invokes
* <code>registerBeanDefinitions</code> on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
if (this.parserClass != null) {
XmlBeanDefinitionParser parser =
(XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
return parser.registerBeanDefinitions(this, doc, resource);
}
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
实际上XmlBeanDefinitionReader把解析注册的工作又转交给BeanDefinitionDocumentReader来完成了。
这里getRegistry()实际上返回的是上一节介绍的生成的DefaultListableBeanFactory,这个工厂实际上是在生成XmlBeanDefinitionReader对象时通过构造方法入参传入的。
这里的createReaderContext(Resource resource)应该特别注意一下,这个上下文中包含了很多重要的信息,以提供给BeanDefinitionDocumentReader来使用。
/**
* Create the {@link XmlReaderContext} to pass over to the document reader.
*/
protected XmlReaderContext createReaderContext(Resource resource) {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, this.namespaceHandlerResolver);
}
二:我们来看看DefaultBeanDefinitionDocumentReader是如何来registerBeanDefinitions的.
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
{
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
preProcessXml(root);
parseBeanDefinitions(root, delegate);
postProcessXml(root);
}
可以看出DefaultBeanDefinitionDocumentReader又把解析注册的工作委托给BeanDefinitionParserDelegate来处理了,具体的工作见parseBeanDefinitions方法.
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
{
if(delegate.isDefaultNamespace(root.getNamespaceURI()))
{
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); i++)
{
org.w3c.dom.Node node = nl.item(i);
if(node instanceof Element)
{
Element ele = (Element)node;
String namespaceUri = ele.getNamespaceURI();
if(delegate.isDefaultNamespace(namespaceUri))
parseDefaultElement(ele, delegate);
else
delegate.parseCustomElement(ele);
}
}
} else
{
delegate.parseCustomElement(root);
}
}
我们继续来看一下核心操作parseDefaultElement(ele,delegate)方法。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
{
if(DomUtils.nodeNameEquals(ele, "import"))
importBeanDefinitionResource(ele);
else
if(DomUtils.nodeNameEquals(ele, "alias"))
processAliasRegistration(ele);
else
if(DomUtils.nodeNameEquals(ele, "bean"))
processBeanDefinition(ele, delegate);
}
Xml配置文件中的bean的解析注册就在processBeanDefinition中了。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
{
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if(bdHolder != null)
{
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try
{
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch(BeanDefinitionStoreException ex)
{
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
Spring把每一个Bean结点解析之后,生成底层的数据结构BeanDefinition,然后存储在BeanDefinitionHold中,很明显具体的真正的解析工作放在parseBeanDefinitionElement方法中,解析完成后则调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())把beanDefinition注册到beanFactory中。下面,我们来看一下核心方法parseBeanDefinitionElement的源码.
三:我们来看看BeanDefinitionParserDelegate的parseBeanDefinitionElement(Element ele)是如何来解析的.
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele)
{
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)
{
String id = ele.getAttribute("id");
String nameAttr = ele.getAttribute("name");
List aliases = new ArrayList();
if(StringUtils.hasLength(nameAttr))
{
String nameArr[] = StringUtils.tokenizeToStringArray(nameAttr, ",; ");
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if(!StringUtils.hasText(beanName) && !aliases.isEmpty())
{
beanName = (String)aliases.remove(0);
if(logger.isDebugEnabled())
logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases");
}
if(containingBean == null)
checkNameUniqueness(beanName, aliases, ele);
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if(beanDefinition != null)
{
if(!StringUtils.hasText(beanName))
try
{
if(containingBean != null)
{
beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, readerContext.getRegistry(), true);
} else
{
beanName = readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if(beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} else
{
return null;
}
}
在这里我们看到对id,name等属性的解析,最核心的操作为 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
/**
* Parse the bean definition itself, without regard to name or aliases. May return
* <code>null</code> if problems occured during the parse of the bean definition.
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
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;
}
在这段代码中,你可清楚的看到对bean组件相关属性和子标记的解析和处理(根据方法的命名).
四:在这里,可以重点关注一下ParseState
public final class ParseState
{
public static interface Entry
{
}
public ParseState()
{
state = new Stack();
}
private ParseState(ParseState other)
{
state = (Stack)other.state.clone();
}
public void push(Entry entry)
{
state.push(entry);
}
public void pop()
{
state.pop();
}
public Entry peek()
{
return (Entry)(state.empty() ? null : state.peek());
}
public ParseState snapshot()
{
return new ParseState(this);
}
public String toString()
{
StringBuffer sb = new StringBuffer();
for(int x = 0; x < state.size(); x++)
{
if(x > 0)
{
sb.append('\n');
for(int y = 0; y < x; y++)
sb.append('\t');
sb.append("-> ");
}
sb.append(state.get(x));
}
return sb.toString();
}
private static final char TAB = 9;
private final Stack state;
}
这个类用了比较巧妙的方式来跟踪解析过程中的相关步骤,用户详见源码和API。
Simple Stack
-based structure for tracking the logical position during a parsing process. entries
are added to the stack at each point during the parse phase in a reader-specific manner.
Calling toString()
will render a tree-style view of the current logical position in the parse phase. This representation is intended for use in error messages.