spring源码深入阅读分析--XmlBeanDefinitionReader(三)

本文详细介绍了Spring框架中XmlBeanDefinitionReader如何读取、解析XML配置文件并注册BeanDefinition。从Resource到BeanDefinition的过程涉及ClassPathResource、DocumentLoader、BeanDefinitionReader等多个关键对象。解析流程包括Resource转化为Document,Document解析为BeanDefinition,并通过BeanDefinitionParserDelegate进行注册。通过跟踪代码,深入理解Spring的内部工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

XmlBeanDefinitionReader读取xml配置文件

XmlBeanFactory对DefaultListableBeanFactory进行了扩展,主要是对xml配置文件的读取得到BeanDefinition,对bean的注册和获取,主要是通过继承父类DefaultListableBeanFactory的方法,唯独与父类不同的是,增加了个性化属性XmlBeanDefinitionReader属性,该属性的作用是对xml文档的读取解析
那么对于spring配置文件的读取,解析及注册的主线需要使用的对象如下:
1、Resource:抽象了spring内部所使用到的底层资源,file,class,classPath等
2、ClassPathResource:根据给定的文件地址,返回对应的resource
3、BeanDefinitionReader:定义资源文件获取并转化成相应的BeanDefinition
4、EnvironmentCapable:获取environment的方法,定义了一个getEnvironmen()无参方法
5、DocumentLoader:加资源文件加载并转化为Document文件,其实现类为DefaultDocumentLoader
6、AbstactBeanDefinitionReader:综合了EnvironmentCapable与BeanDefinitionReader功能并进行实现
7、BeanDefinitionDocumentReader:读取document文件转化为beanDefinition对象,并进行注册
8、BeanDefinitionParserDelegate:定义解析Element的方法

XmlBeanDefinitionReader的类继承实现关系如下:

在这里插入图片描述ClassPathResource的类继承实现如下:

在这里插入图片描述通过对以上对象的分析,大致可以弄懂xml配置文件读取的流程。
1、通过ClassPathResource,将给定的路径文件名称,转化成Resource
2、通过XmlBeanDefinitionReader中属性 DocumentLoader将resource加载并转化为Document文件
3、通过接口BeanDefinitionDocumentReader的实现DefaultBeanDefinitionDocumentReander包含的BeanDefinitionParserDelegate对象对document进行解析,转化为beanDefinition对象进行注册,注册就是放入一个map中,Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

现在根据下面代码debug进入,看看XmlBeanDefinitionReader类,内部做了什么

Resource resource= new ClassPathResource("beanFactoryTest.xml");
BeanFactory bf = new XmlBeanFactory(resource);

跟踪进入下列方法

public XmlBeanFactory(Resource resource) throws BeansException {
   this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
   super(parentBeanFactory);
   this.reader.loadBeanDefinitions(resource);
}

在此处调用了父类的构造方法

public AbstractAutowireCapableBeanFactory() {
   super();
   /**
    * 忽略给定接口的自动装配功能
    * 为什么要这么做呢?
    * 当A中有属性B,当spring在获取A的属性B的时候,B还没有初始化,那么spring会初始化B实例,但是在某些情况下,spring不会初始化B,如B实现了BeanNameWare接口,
    * spring给的介绍为:
    *     自动装配时,忽略给定的依赖接口,典型的应用是通过其他方式解析Application上下文注册依赖,类似于BeanFactory通过BeanFactoryWare进行诸如
    *     或者ApplicationContext通过ApplicationContextWare进行注入
    */
   ignoreDependencyInterface(BeanNameAware.class);
   ignoreDependencyInterface(BeanFactoryAware.class);
   ignoreDependencyInterface(BeanClassLoaderAware.class);
}

到达XmlBeanDefinitionReader的loadBeanDefinitins(resource)方法

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

   //通过属性记录已经加载的资源
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<>(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 {
         //2、获取输入流,从resouce中获取inputStream,并构造inputSource
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         //3、通过resource及构造的inputSource调用,进入核心逻辑doLoadBeanDefinitions
         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();
      }
   }
}

spring中,很多真正干事情的方法函数,前面都带有do,进入XmlBeanDefinitionReader的doLoadBeanDefinitions方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      //1、对xml进行验证 2、加载document 返回doccument对象
      Document doc = doLoadDocument(inputSource, resource);
      //3、根据返回的document注册bean信息
      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);
   }
}

上面的第一步,对xml进行验证

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}

其中的getValidationModeForResource方法

protected int getValidationModeForResource(Resource resource) {
   //如果通过XmlBeanDefinitionReader 的setValidationMode设置了验证模式,则使用设置的验证模式

   /**
    *  配置xml文件分为XSD(XML Schemas Definition)和DTD(Document Type Definition)
    *   <!DOCTYPE beans PUBLIC "-//Sp口 ng//DTD BEAN 2 0//EN" dtd/ Spring-beans-2 O.dtd">
    *
    *   <beans xmlns="http://www.springframework.org/schema/beans"
    *        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    *        xsi:schemaLocation="http://www.springframework.org/schema/beans
    *        http://www.springframework.org/schema/beans/spring-beans.xsd">
    *
    *
    *   两种类型可以根据是否包含DOCTYPE来判断是DTD 还是XSD模式
    */
   int validationModeToUse = getValidationMode();
   if (validationModeToUse != VALIDATION_AUTO) {
      return validationModeToUse;
   }
   //如果没有设置,则自动检测
   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;
}

使用DefaultDocumentLoader的loadDocument方法将resource转为document文件

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

   DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
   if (logger.isDebugEnabled()) {
      logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
   }
   DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
   return builder.parse(inputSource);
}

这个转换过程没有什么特殊的,感兴趣的话可以进去,现在跳过,主要看看XmlBeanDefinitionReader中的registryBeanDefinitions方法

//解析及注册BeanDefinition
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   //使用默认的DefaultBeanDefinitionDocumentReader 初始化BeanDefinitionReader
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   //记录统计前BeanDefinition的个数
   int countBefore = getRegistry().getBeanDefinitionCount();
   //加载bean,这个地方是重点
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   //本次加载的BeanDefinition的个数
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

进入DefualtBeanDefinitionDocumentReader类的registryBeanDefinitions方法中

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   //提取root
   Element root = doc.getDocumentElement();
   //核心部分,真正开始解析
   doRegisterBeanDefinitions(root);
}

现在我们要进入真正的解析过程了,前面的步骤都是准备阶段,进入DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法中

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

   if (this.delegate.isDefaultNamespace(root)) {
      //处理profile属性
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.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(root);
   //解析
   parseBeanDefinitions(root, this.delegate);
   //解析后的处理,留给子类实现
   postProcessXml(root);

   this.delegate = parent;
}

进入parseBeanDefinitions方法中

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   //默认命名空间进行处理
   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)) {
               //对各种默认的标签进行处理,比如 alias,import,bean,beans等spring中定义的标签
               parseDefaultElement(ele, delegate);
            }
            else {
               //自定义命名空间进行处理 比如使用aop需要在配置文件中使用的<aop:aspectj-autoproxy/> aop标签
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      //自定义命名空间进行处理
      delegate.parseCustomElement(root);
   }
}

进入DefaultBeanDefinitionDocumentReader的parseDefaultElement方法,对默认标签的解析

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);
   }
   //对bean标签进行处理 该标签的解析最为复杂,了解了该标签的解析过程,其他的标签的解析迎刃而解
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   //对beans标签进行处理
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

我们下面分析对于bean的解析过程,进入DefaultBeanDefinitionDocumentReader的processBeanDefinition(ele,delegate)方法中

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //1、通过委托BeanDefinitionParserDelegate类的parserBeanDefinitionElement()方法对ele进行解析,返回的BeanDefinitionHolder
   //实例bdHolder,实例bdHolder中包含class,name,id ,alias等属性
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      //2、bdHolder默认不为空时,bdHolder中包含自定义的属性,对自定义的属性进行解析
      /**
       * 适用的场景如下
       * <bean id="test" class="test.MyClass">
       *     <myBean:user username="aaa"></myBean:user>
       * </bean>
       *
       * 如果默认标签下还存在自定义的标签,需要对自定义的<myBean></myBean>标签进行解析
       */
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         //3、解析完成之后对bdHolder进行注册
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      //4、最后发出响应事件,通知相关的监听器
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

processBeanDefinition方法的主要操作如下:
1、通过BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法对ele进行解析,并返回包含BeanDefinition,alias,name ,id等属性的BeanDefinitonHolder对象
2、如果在默认标签下还存在自定义的子标签,则需要对子标签进行解析,也放入到BeanDefinitionHolder中
3、通过BeanDefinitionReaderUtils工具类的registerBeanDefinition方法将beanDefinitionHolder进行注册
4、最后发出响应事件,通知相关的监听器,相关的bean已经注册成功了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值