2.5容器的基础XmlBeanFactory

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));
初始化XmlBeanFactory逻辑的时序图:
2.5.1读取配置文件(资源)的方式
对于不同来源的资源文件都有相应的Resource实现。文件(FileSystemResource)、ClassPath资源(ClassPathResource)、URL资源(URLResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResoure)。
public XmlBeanFactory(Resource resource) throws BeansException {
    //构造函数内部再次调用构造函数XmlBeanFactory(Resource resource,BeanFactory parentBeanFactory)
    this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    //parentBeanFactory可以为空
    //跟踪代码到父类AbstractAutowireCapableBeanFactory的构造方法
    super(parentBeanFactory);
    this.reader.loadBeanDefinitions(resource);
}

AbstractAutowireCapableBeanFactory
public AbstractAutowireCapableBeanFactory() {
    super();
    //ignoreDependencyInterface的主要功能还是忽略给定接口的自动装配功能。
    //举例来说就是A中有属性B,如果Spring获取A的Bean时,如果属性属性B还没有
    //初始化,那么Spring就会自动初始化B,这也是Spring提供的一个重要特性。
    //但是,某些情况下B不会初始化,其中一种情况就是B实现了BeanNameAware接口。
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
    if (IN_NATIVE_IMAGE) {
        this.instantiationStrategy = new SimpleInstantiationStrategy();
    }
    else {
         this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
    }
}

 

2.5.2加载Bean
1.封装资源文档。当进入XmlBeanDefinition后首先对参数Resource使用EncodedResource类进行封装。
2.获取输入流。从Resource中获取对应InputStream并构造InputSource。
3.通过InputSource实例与Resource实例继续调用doLoadBeanDefinitions。
loadBeanDefinitions函数的实现:
XmlBeanDefinitionReader
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
}
EncodedResource
public EncodedResource(Resource resource) {
    this(resource, null, null);
}
EncodedResource
private EncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset) {
    super();
    Assert.notNull(resource, "Resource must not be null");
    this.resource = resource;
    this.encoding = encoding;
    this.charset = charset;
}

 

EncodedResource为Resource设置了编码。
下面是数据准备阶段:
XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Loading XML bean definitions from " + encodedResource);
    }
    //通过属性来记录已经加载过的资源
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    //从encodedResource中获取已经封装的Resource对象并再次从Resource中获取其中的InputStream
    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
        //InputSource不是来源于Spring,它的全路径是org.xml.sax.InoutSource
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        //进入真正的逻辑核心部分
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

 

首先对Resource进行封装,考虑到Resource可能存在编码要求的情况。其次通过SAX的方式读取XML文件来准备InputSource对象。最后将准备的数据传入核心处理部分doLoadBeanDefinition。
核心方法doLoadBeanDefinition:
XmlBeanDefinitionReader
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

    try {
        Document doc = doLoadDocument(inputSource, resource);
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        return count;
    }
    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);
    }
}
XmlBeanDefinitionReader
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }
在上面冗余的代码中如果不考虑异常代码,其实就做了三件事:
  • 获取对XML文件的验证模式
  • 加载XML文件,并得到对应的Document
  • 根据返回的Document注册Bean信息
下面我们开始获取XML文件的验证模式。
 





posted @ 2021-01-13 16:41  _Shing  阅读(265)  评论(0)    收藏  举报