Spring源码分析之配置文件解析(二)

找到了资源文件,接下来就是解析资源文件了,具体的解析工作是从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<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  if (currentResources == null) {
   currentResources = new HashSet<EncodedResource>(4);
   this.resourcesCurrentlyBeingLoaded.set(currentResources);
  }
  if (!currentResources.add(encodedResource)) {
   throw new BeanDefinitionStoreException(
     “Detected cyclic 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.remove();
   }
  }
 }

这个方法里最核心的操作是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;
  }
  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对象,注册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 {
  // 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的.

/**
  * Parses bean definitions according to the “spring-beans” DTD.
  * <p>Opens a DOM Document; then initializes the default settings
  * specified at <code>&lt;beans&gt;</code> level; then parses
  * the contained bean definitions.
  */
 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);
 }

我们继续来看一下核心操作parseDefaultElement(ele,delegate)方法。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
   importBeanDefinitionResource(ele);
  }
  else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
   processAliasRegistration(ele);
  }
  else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
   processBeanDefinition(ele, delegate);
  }
 }
Xml配置文件中的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 bdHolder = delegate.parseBeanDefinitionElement(ele);
  if (bdHolder != null) {
   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));
  }
 }
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_ATTRIBUTE);
  String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

  List<String> aliases = new ArrayList<String>();
  if (StringUtils.hasLength(nameAttr)) {
   String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
   aliases.addAll(Arrays.asList(nameArr));
  }

  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”);
   }
  }

  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, this.readerContext.getRegistry(), true);
     }
     else {
      beanName = this.readerContext.generateBeanName(beanDefinition);
      // Register an alias for the plain bean class name, if still possible,
      // if the generator returned the class name plus a suffix.
      // This is expected for Spring 1.2/2.0 backwards compatibility.
      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);
   return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  }

  return null;
 }
在这里我们看到对id,name等属性的解析,最核心的操作为    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
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 {

 /**
  * Tab character used when rendering the tree-style representation.
  */
 private static final char TAB = ‘\t’;

 /**
  * Internal {@link Stack} storage.
  */
 private final Stack state;

 /**
  * Create a new <code>ParseState</code> with an empty {@link Stack}.
  */
 public ParseState() {
  this.state = new Stack();
 }

 /**
  * Create a new <code>ParseState</code> whose {@link Stack} is a {@link Object#clone clone}
  * of that of the passed in <code>ParseState</code>.
  */
 private ParseState(ParseState other) {
  this.state = (Stack) other.state.clone();
 }

 /**
  * Add a new {@link Entry} to the {@link Stack}.
  */
 public void push(Entry entry) {
  this.state.push(entry);
 }

 /**
  * Remove an {@link Entry} from the {@link Stack}.
  */
 public void pop() {
  this.state.pop();
 }

 /**
  * Return the {@link Entry} currently at the top of the {@link Stack} or
  * <code>null</code> if the {@link Stack} is empty.
  */
 public Entry peek() {
  return (Entry) (this.state.empty() ? null : this.state.peek());
 }

 /**
  * Create a new instance of {@link ParseState} which is an independent snapshot
  * of this instance.
  */
 public ParseState snapshot() {
  return new ParseState(this);
 }

 /**
  * Returns a tree-style representation of the current <code>ParseState</code>.
  */
 @Override
 public String toString() {
  StringBuilder sb = new StringBuilder();
  for (int x = 0; x < this.state.size(); x++) {
   if (x > 0) {
    sb.append(‘\n’);
    for (int y = 0; y < x; y++) {
     sb.append(TAB);
    }
    sb.append(“-> “);
   }
   sb.append(this.state.get(x));
  }
  return sb.toString();
 }

 /**
  * Marker interface for entries into the {@link ParseState}.
  */
 public interface Entry {

 }

}
这个类用了比较巧妙的方式来跟踪解析过程中的相关步骤,用户详见源码和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.

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/hai19850514/article/details/84407241
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞