1. XML读取

一、Xml 文件读取流程

1. 通过继承自 AbstractBeanDefinitionReader 中的方法,来使用 ResourceLoader 将资源文件路径转换为对应的 Resource 文件;

2. 通过 DocumentLoader 对 Resource 文件进行转换,将 Resource 文件转换为 Document 文件;

3. 通过实现接口 BeanDefinitionDocumentReader 的 DefaultBeanDefinitionDocumentReader 类对 Document 进行解析,并使用 BeanDefinitionParserDelegate 对 Element 进行解析。

 二、源代码阅读入口

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
BookService bookService = (BookService) bf.getBean("bookService");

三、源码解析

1. Resource创建

  创建 Resource 对象,并对成员变量 path、classLoader(用于加载文件流:java.lang.ClassLoader#getResourceAsStream(String)) 赋值。

public ClassPathResource(String path) {
    this(path, (ClassLoader) null);
}

public ClassPathResource(String path, ClassLoader classLoader) {
    Assert.notNull(path, "Path must not be null");
    String pathToUse = StringUtils.cleanPath(path);
    if (pathToUse.startsWith("/")) {
        pathToUse = pathToUse.substring(1);
    }
    this.path = pathToUse;
    this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

扩展:Resource 有多种实现类,用于读取不同场景下的文件。读取文件(FileSystemResource)、读取Classpath资源(ClassPathResource)、读取Url资源(UrlResource)、读取InputStream资源(InputStreamResource),等等。

2. 忽略给定接口自动装配功能

  先说结论:将影响初始化的条件先忽略掉。

  举个例子:在Bean初始化时,如果存在 A 依赖 B,Spring会自动初始化 B,但是当 B 实现了 BeanNameAware 接口时,就无法初始化,这个时候,我们就需要先将影响初始化的条件忽略掉。

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
    public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
        super(parentBeanFactory);
    }
    public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
        this();
        setParentBeanFactory(parentBeanFactory);
    }
    public AbstractAutowireCapableBeanFactory() {
        super();
        ignoreDependencyInterface(BeanNameAware.class);
        ignoreDependencyInterface(BeanFactoryAware.class);
        ignoreDependencyInterface(BeanClassLoaderAware.class);
    }

3. 将 Resource 转换为 Document 文件

1. 使用 EncodedResource 对 Resource 进行封装,对资源文件进行编码处理

2. 使用 ThreadLocal 检测是否存在循环加载

3. 从 Resource 中获取 InputStream 构造 InputSource

4. 将 Resource 转换为 Document(DOM解析)

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(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.getResource());
        }
        
        // resourcesCurrentlyBeingLoaded是一个ThreadLocal
        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());
                }
                // 解析Xml文件
                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();
            }
        }
    }
    
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            Document doc = doLoadDocument(inputSource, resource);
            return registerBeanDefinitions(doc, resource);
        }
        // ...
        // 一堆catch和throw
    }
    
    // getValidationModeForResource(resource):获取Xml验证格式(DTD or XSD)
    // getEntityResolver():获取EntityResolver(该类用于自定义寻找DTD文件方法,DTD文件默认从网络读取)
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }
    
    // 创建Document并解析,创建Factory,然后再创建实例,最后通过Source转换为Document(DOM解析)
    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);
    }

4. 解析Xml

1. 解析前准备:创建documentReader、记录之前的BeanDefinition个数

2. 处理Profile属性,如果当前文件环境与Profile不一致,则不解析

3. 对root节点进行解析:

  3.1 对于beans节点,使用bean解析

  3.2 针对其他类型节点,使用自定义解析器解析

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 设置环境变量,XmlBeanDefinitionReader初始化时创建的
        documentReader.setEnvironment(getEnvironment());
        // 记录之前加载的BeanDefinition个数
        int countBefore = getRegistry().getBeanDefinitionCount();
        // Bean加载
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        // 返回本次加载的BeanDefinition个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        // 解析 root 下面所有节点
        doRegisterBeanDefinitions(root);
    }
    
    protected void doRegisterBeanDefinitions(Element root) {
        //专门处理解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        
        // 处理profile属性,如果当前环境与profile不同,则不解析
        if (this.delegate.isDefaultNamespace(root)) {
            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)) {
                    return;
                }
            }
        }
        
        // 解析前处理,由子类实现
        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        // 解析后处理,由子类实现
        postProcessXml(root);

        this.delegate = parent;
    }
    
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 判断节点是否为beans(使用节点的命名空间与Spring固定的命名空间进行比较http://www.springframework.org/schema/beans,如果一致说明是默认的,否则是自定义标签)
        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)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        // 自定义标签解析,例如 <tx:annotation-driven />
        else {
            delegate.parseCustomElement(root);
        }
    }
    

 

posted @ 2019-10-23 17:58  莹狼  阅读(262)  评论(0编辑  收藏  举报