首页 > 学院 > 开发设计 > 正文

Spring IOC 源码阅读资源加载和注册

2019-11-11 05:24:33
字体:
来源:转载
供稿:网友

   上面讲到,sPRing在查找到资源以后,在BeanDefinitionReader的loadBeanDefinitions(String location)方法中,接着就调用了int loadCount = loadBeanDefinitions(resources);

   这个方法间接的调用了子类xmlBeanDifinitionReader的loadBeanDefinitions(EncodedResource encodedResource)方法:

[java] view plain copy <span style="font-size:12px;">/**      * 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());                  }                  <span style="color:#ff0000;">return doLoadBeanDefinitions(inputSource, encodedResource.getResource());</span>              }              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();              }          }      }</span>  

上述方法调用了doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法:

 

[java] view plain copy /**      * 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);              <span style="color:#ff0000;">Document doc = this.documentLoader.loadDocument(                      inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());              return registerBeanDefinitions(doc, resource);  </span>     }          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);          }      }  

在上面的方法中将xml人间解析成了document对象,然后调用了registerBeanDefinitions(Document doc, Resource resource)方法:

[java] view plain copy     /**      * 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 {          BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();          documentReader.setEnvironment(this.getEnvironment());          int countBefore = getRegistry().getBeanDefinitionCount();          <span style="color:#ff0000;">documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  </span>     return getRegistry().getBeanDefinitionCount() - countBefore;      }  

在上面的方法中先创建了一个DocumentReader对象,然后调用DefaultBeanDifinitionDocumentReader的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)方法,此方法调用了下面的方法

[java] view plain copy /**  * Register each bean definition within the given root {@code <beans/>} element.  * @throws IllegalStateException if {@code <beans profile="..."} attribute is present  * and Environment property has not been set  * @see #setEnvironment  */  protected void doRegisterBeanDefinitions(Element root) {      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);      if (StringUtils.hasText(profileSpec)) {          Assert.state(this.environment != null, "environment property must not be null");          String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);          if (!this.environment.acceptsProfiles(specifiedProfiles)) {              return;          }      }        // 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;      this.delegate = createHelper(readerContext, root, parent);        preProcessXml(root);      <span style="color:#ff0000;">parseBeanDefinitions(root, this.delegate);  /span>      postProcessXml(root);        this.delegate = parent;  }  

在这个方法中创建了解析beandefinition的委托对象,实际上所有的解析都是在这个委托对象中完成的。

再看上面的方法间接调用了下面的DefaultBeanDifinitionDocumentReader方法:

[java] view plain copy /**  * Process the given bean element, parsing the bean definition  * and registering it with the registry.  */  protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {      <span style="color:#ff0000;">BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);</span>      if (bdHolder != null) {          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);          try {              // Register the final decorated instance.              <span style="color:#ff0000;">BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  /span>          }          catch (BeanDefinitionStoreException ex) {              getReaderContext().error("Failed to register bean definition with name '" +                      bdHolder.getBeanName() + "'", ele, ex);          }          // Send registration event.          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));      }  }  

上面红色的的两个方法一个是解析得到BeanDefinitionHolder对象,此对象持有BeanDefinition对象和bean的别名,id等信息。一个方法是注册解析得到的BeanDefinition。

 下面看看在BeanDefinitionParserDeleget类中的解析方法,

[html] view plain copy /**   * Parses the supplied <code><bean></code> element. May return <code>null</code>   * if there were errors during parse. Errors are reported to the   * {@link org.springframework.beans.factory.parsing.ProblemReporter}.   */  public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {      return parseBeanDefinitionElement(ele, null);  }    /**   * Parses the supplied <code><bean></code> element. May return <code>null</code>   * if there were errors during parse. Errors are reported to the   * {@link org.springframework.beans.factory.parsing.ProblemReporter}.   */  public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {      //这里取得<bean>中定义的 id name和 aliase的值  [html] view plain copy String id = ele.getAttribute(ID_ATTRIBUTE);  = ele.getAttribute(NAME_ATTRIBUTE);  [html] view plain copy         List<String> aliases = new ArrayList<String>();          if (StringUtils.hasLength(nameAttr)) {              String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_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);          }                    //这个方法是对bean元素的详细解析          <span style="color:#ff0000;">AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  </span>     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;      }  看看详细的解析方法: parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean)[html] view plain copy /**   * Parse the bean definition itself, without regard to name or aliases. May return   * <code>null</code> if problems occurred during the parsing of the bean definition.   */  public AbstractBeanDefinition parseBeanDefinitionElement(          Element ele, String beanName, BeanDefinition containingBean) {        this.parseState.push(new BeanEntry(beanName));                   //取得class name的定义,这里指读取<bean>中设置的class的名字,记录到BeanDefinition中,并不涉及对象的实例化过程,实例化实际上是在以来注入的时候完成的      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);          }          <span style="color:#ff0000;">AbstractBeanDefinition bd = createBeanDefinition(className, parent);                            //对当前的Bean元素进行属性解析,并设置description的信息  /span>          parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);          bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));                            //从名字可以看出。这里是对各种<bean>元素的信息进行解析的地方          parseMetaElements(ele, bd);          parseLookupOverrideSubElements(ele, bd.getMethodOverrides());          parseReplacedMethodSubElements(ele, bd.getMethodOverrides());                            //解析<bean>的构造函数          parseConstructorArgElements(ele, bd);          //解析bean的property设置  [html] view plain copy                          <span style="color:#ff0000;">parsePropertyElements(ele, bd);  span>           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;  

上面主要关注红色的两个方法,一个是创建了BeanDefinition,一个是解析详细的bean的属性设置。

创建BeanDefinition是在调用了类BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());方法

[java] view plain copy /**  * Create a new GenericBeanDefinition for the given parent name and class name,  * eagerly loading the bean class if a ClassLoader has been specified.  * @param parentName the name of the parent bean, if any  * @param className the name of the bean class, if any  * @param classLoader the ClassLoader to use for loading bean classes  * (can be <code>null</code> to just register bean classes by name)  * @return the bean definition  * @throws ClassNotFoundException if the bean class could not be loaded  */  public static AbstractBeanDefinition createBeanDefinition(          String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {        GenericBeanDefinition bd = new GenericBeanDefinition();      bd.setParentName(parentName);      if (className != null) {          if (classLoader != null) {              bd.setBeanClass(ClassUtils.forName(className, classLoader));          }          else {              bd.setBeanClassName(className);          }      }      return bd;  }  

再看详细解析bean的属性定义的方法:

 

[html] view plain copy /**   * Parse property sub-elements of the given bean element.   */  public void parsePropertyElements(Element beanEle, BeanDefinition bd) {      NodeList nl = beanEle.getChildNodes();      for (int i = 0; i < nl.getLength(); i++) {          Node node = nl.item(i);          if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {              <span style="color:#ff0000;">parsePropertyElement((Element) node, bd);  /span>          }      }  }  

 

[java] view plain copy /**  * Parse a property element.  */  public void parsePropertyElement(Element ele, BeanDefinition bd) {      //取得property的名字  [java] view plain copy                 String propertyName = ele.getAttribute(NAME_ATTRIBUTE);  if (!StringUtils.hasLength(propertyName)) {      error("Tag 'property' must have a 'name' attribute", ele);      return;  }  this.parseState.push(new PropertyEntry(propertyName));  try {        //如果在同一个Bean中已经有同名的property存在,则不进行解析,直接返回,也就是说,在同一个bean中的相同property设置,只有第一个起作用  [java] view plain copy                       if (bd.getPropertyValues().contains(propertyName)) {  error("Multiple 'property' definitions for property '" + propertyName + "'", ele);  return;                           //这里是解析property的地方,返回的对象对应对bean定义的property属性设置的解析结果,这个解析结果会被封装到PropertyValue中  [java] view plain copy     <span style="color:#ff0000;">Object val = parsePropertyValue(ele, bd, propertyName);  span>           <span style="color:#009900;">PropertyValue pv = new PropertyValue(propertyName, val);      parseMetaElements(ele, pv);      pv.setSource(extractSource(ele));      bd.getPropertyValues().addPropertyValue(pv);  span>       }  finally {      this.parseState.pop();  }  [java] view plain copy    [java] view plain copy 下面这个方法是去的Property元素的值,也许是MAP,LIST SET或者其他ref对象  [python] view plain copy /**   * Get the value of a property element. May be a list etc.   * Also used for constructor arguments, "propertyName" being null in this case.   */  public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {      String elementName = (propertyName != null) ?                      "<property> element for property '" + propertyName + "'" :                      "<constructor-arg> element";        // Should only have one child element: ref, value, list, etc.      NodeList nl = ele.getChildNodes();      Element subElement = null;      for (int i = 0; i < nl.getLength(); i++) {          Node node = nl.item(i);          if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&                  !nodeNameEquals(node, META_ELEMENT)) {              // Child element is what we're looking for.              if (subElement != null) {                  error(elementName + " must not contain more than one sub-element", ele);              }              else {                  subElement = (Element) node;              }          }      }                   //这里判断property的属性是ref还是value,不允许同时是ref和value      boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);      boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);      if ((hasRefAttribute && hasValueAttribute) ||              ((hasRefAttribute || hasValueAttribute) && subElement != null)) {          error(elementName +                  " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);      }                   //如果是ref,创建一个ref的数据对象RuntimeBeanReference 这个对象封装 了ref的信息      if (hasRefAttribute) {          String refName = ele.getAttribute(REF_ATTRIBUTE);          if (!StringUtils.hasText(refName)) {              error(elementName + " contains empty 'ref' attribute", ele);          }          RuntimeBeanReference ref = new RuntimeBeanReference(refName);          ref.setSource(extractSource(ele));          return ref;      }                   //如果是value,创建一个value的数据对象TypeStringValue,这个对象封装了value信息  [python] view plain copy else if (hasValueAttribute) {      TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));      valueHolder.setSource(extractSource(ele));      return valueHolder;  }  //如果有子元素,对子元素解析,这里是对Map、List、Set、Property等元素解析  [python] view plain copy                 else if (subElement != null) {          return parsePropertySubElement(subElement, bd);      }      else {          // Neither child element nor "ref" or "value" attribute found.          error(elementName + " must specify a ref or value", ele);          return null;      }  }  

上述解析过程可简单的看成是

1、XmlBeanDefinitionReader读取资源,

2、交给DefaultBeanDefinitionDocumentReader对象解析成Document对象,

3、然后DefaultBeanDefinitionDocumentReader对象委托给BeanDefinitionParserDelegate对象解析成BeanDefinitionHoler对象。

4、得到返回的BeanDefunitionHolder对象后调用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());方法进行注册。

getReaderContext().getRegistry()这里得到的是DefaultListableBeanFactory。在AbstarctXmlapplicationContext的创建XmlBeandefinitionReader的地方可以看到:

[java] view plain copy XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  

 

DefaultListableBeanFactory中注册的地方

 

[java] view plain copy public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)          throws BeanDefinitionStoreException {        Assert.hasText(beanName, "Bean name must not be empty");      Assert.notNull(beanDefinition, "BeanDefinition must not be null");        if (beanDefinition instanceof AbstractBeanDefinition) {          try {              ((AbstractBeanDefinition) beanDefinition).validate();          }          catch (BeanDefinitionValidationException ex) {              throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,                      "Validation of bean definition failed", ex);          }      }                    注册的地方需要同步,保持一致性      synchronized (this.beanDefinitionMap) {                            //这里检查是不是有相同名字的BeanDefinition已经存在于IOC容器了,如果有相同的名字的BeanDefinition,但又不允许覆盖,会抛出异常  [java] view plain copy Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);  if (oldBeanDefinition != null) {      if (!this.allowBeanDefinitionOverriding) {          throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,                  "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +                  "': There is already [" + oldBeanDefinition + "] bound.");      }      else {          if (this.logger.isInfoEnabled()) {              this.logger.info("Overriding bean definition for bean '" + beanName +                      "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");          }      }  }  else {      //这是正常的注册BeanDefinition的过程,把Bean的名字存入到BeanDefinitionNames的同时,beanName作为Map的Key,吧BeanDefinition作为value存入到IOC容器的BeanDefinitionMap中  [java] view plain copy                                      this.beanDefinitionNames.add(beanName);                  this.frozenBeanDefinitionNames = null;              }              <span style="color:#ff0000;">this.beanDefinitionMap.put(beanName, beanDefinition);  </span>     }            resetBeanDefinition(beanName);      }  

上面的代码可以看出IOC容器是作为一个Map实现的。看看DefaultListableBeanFactory中有关容器定义

 

 /** Map from dependency type to corresponding autowired value */ private final Map<Class<?>, Object> resolvableDependencies = new HashMap<Class<?>, Object>();

 /** 这个就是存放BeanDefinition的容器MAP */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

 /** 存放单例的类的名字 */ private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();

 /** Map of non-singleton bean names keyed by bean class */ private final Map<Class<?>, String[]> nonSingletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();

 /** 容器中的beanName集合 */ private final List<String> beanDefinitionNames = new ArrayList<String>();

容器启动时候完成的事情就已经全部分析完成了,类的实例化是在依赖注入或根据配置在容器启动的时候完成的。容器启动加载完成后,就可以再getBean方法调用的时候去使用了。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表