spring 的XmlBeanFactory源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取 2000+ 道 Java 面试题

XmlBeanFactory继承自DefaultListableBeanFactory,相比DefaultListableBeanFactory,它能解析Xml配置文件,因为它持有XmlBeanDefinitionReader,其构造方法调用了loadBeanDefinition方法

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
       return loadBeanDefinitions(new EncodedResource(resource));
    }
    loadBeanDefinition方法调用了下面这个方法
    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();
          }
       }
    }
    通过查看代码可以看出改方法做了几件事情:
    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!");
    }

获取线程的本地变量

    resourcesCurrentlyBeingLoaded

    resourcesCurrentlyBeingLoaded

保存了当前线程所加载的资源,是一个Set集合

    真正的核心是调用doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    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);
       }
    }
    doLoadBeanDefinitions方法做了两件事情:
    1、doLoadDocument(inputSource, resource);
    实际是调用了DefaultDocumentLoader对象的
    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);
    }
    创建了DocumentBuilderFactory对象
    然后构建了DocumentBuilder 对象
    然后执行DocumentBuilder对象的parse解析方法
    其实说白了就是将一个inputSource解析称一个document对象返回
    具体怎么解析此处就不在跟踪下去,留个疑问,后续在回头看吧
    2、registerBeanDefinitions(doc, resource);
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
       BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
       int countBefore = getRegistry().getBeanDefinitionCount();
       documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
       return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    此方法好像很厉害的样子,
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

此句创建一个BeanDefinitionDocumentReader对象,是怎么创建的呢?

    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
       return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

这个创建方式比较奇葩啊!其实就是通过反射创建一个DefaultBeanDefinitionDocumentReader对象,然后在强转为BeanDefinitionDocumentReader类型的对象

此处等同于

return (BeanDefinitionDocumentReader)new DefaultBeanDefinitionDocumentReader();

为什么要这样子去创建一个BeanDefinitionDocumentReader类型的对象呢?我特么也纳闷。。。。。。容我思考片刻,

如果说是为了增加灵活性,documentReaderClass类型做到可配置化,那也不需要使用放射啊?

设置一个BeanDefinitionDocumentReader的成员变量,改成员变量默认使用DefaultBeanDefinitionDocumentReader

然后在需要修改的使用,对外提供一个public的set方法,传一个需要的BeanDefinitionDocumentReader类型进来不就可以了?为什么要使用反射呢?使用反射效果和这个一样的呀。。。。默认也是实例化DefaultBeanDefinitionDocumentReader.class;在需要更改的时候

    public void setDocumentReaderClass(Class<?> documentReaderClass) {
       if (documentReaderClass == null || !BeanDefinitionDocumentReader.class.isAssignableFrom(documentReaderClass)) {
          throw new IllegalArgumentException(
                "documentReaderClass must be an implementation of the BeanDefinitionDocumentReader interface");
       }
       this.documentReaderClass = documentReaderClass;
    }
    调用setDocumentReaderClass方法去更改。那么使用反射的意义何在?百思不得其解
    难道是为了防止这个类没有还没有被加载? 好像是这么一回事啊,别忘了spring通过XmlBeanFactory对象去读取配置文件,然后实例化对象的,在读取解析和生成DocumentReader之前,怎么能使用set方法传递一个BeanDefinitionDocumentReader类型进去呢?因为这个spring容器还没构建好,所以无法保证这个BeanDefinitionDocumentReader是已经正常初试化的。
    int countBefore = getRegistry().getBeanDefinitionCount();
    获得BeanDefinition定义器注册之前bean的数量
       documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    注册BeanDefinition
    return getRegistry().getBeanDefinitionCount() - countBefore;

返回的是注册前后的数量差

    这里有个疑问 BeanDefinition是什么概念,有什么作用,注册这玩意是干嘛用的呢? 
    总的来说
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    做两件事情,1、加载解析配置文件2、根据配置文件解析后的Document对象注册beanDefinition

来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring 的XmlBeanFactory源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏