spring Ioc源码解读-xml资源加载与解析

Spring对内部使用到的资源比如spring.xml实现了自己的抽象结构,Spring利用Resource接口封装底层资源文件

public interface Resource extends InputStreamSource {

    boolean exists();

    boolean isReadable();

    boolean isOpen();

    URL getURL() throws IOException;

    URI getURI() throws IOException;

    File getFile() throws IOException;

    long lastModified() throws IOException;

    Resource createRelative(String relativePath) throws IOException;

    String getFilename();

    String getDescription();

}

Resource接口继承了InputStreamSource接口

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

这个接口封装任何能够返回InputStream的类,它只有一个方法getInputStream()该方法返回一个新的InputStream对象,Resource接口对不同来源的资源文件都有相应的Resource实现

  • FileSystemResource
  • ClasspathResource
  • UrlResource
  • InputStreamResource
  • ByteArrayResource

有时候我们需要读取classpath路径下的一个文件进行操作,我们可以使用spring提供的ClassPathResource类读取文件

Resource resource = new ClassPathResource("spring.xml");
        InputStream in = resource.getInputStream();

当Resource相关实现类完成了对资源的封装后,我们可以将配置文件的读取工作交给XmlBeanDefinitionReader来处理

XmlBeanFactory调用如下这个构造函数进行初始化

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

XmlBeanFactory依赖关系如下所示

spring Ioc源码解读-xml资源加载与解析_第1张图片

在XmlBeanFactory构造方法中调用了父类DefaultListableBeanFactory的构造方法,而后DefaultListableBeanFactory又调用了AbstractWutowireCapableBeanFactory的构造方法

public AbstractAutowireCapableBeanFactory() {
        super();
        ignoreDependencyInterface(BeanNameAware.class);
        ignoreDependencyInterface(BeanFactoryAware.class);
        ignoreDependencyInterface(BeanClassLoaderAware.class);
    }

ignoreDependencyInterface方法的功能是忽略给定接口的自动装配功能.比如说,当A中有属性B的时候,那么当Spring在获取A的Bean的时候,如果其属性B还没有初始化,那么Spring会自动初始化B,但是如果B实现了BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,spring会忽略对其自动装配,而是通过其他途径对其进行装配

XmlBeanFactory调用完super()父类构造方法后,继续调用this.reader.loadBeanDefinitions(resource);方法加载资源

加载资源是会调用如下的一个方法,

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            int validationMode = getValidationModeForResource(resource);
            Document doc = this.documentLoader.loadDocument(
                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
            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);
        }
    }

这个方法非常的冗长,核心代码try包围的三行,三行代码主要的目的如下
1. 获取对xml文件的验证模式
2. 加载xml文件,并得到相应的Document
3. 根据返回的Document注册bean信息

第三项是注册bean的重点

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //beanMap中已有的bean个数
        int countBefore = getRegistry().getBeanDefinitionCount();
        //注册bean
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回本次注册bean的个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

BeanDefinitionDocumentReader是一个接口用于bean的注册
在本例中调用的是BeanDefinitionDocumentReader的实现之一DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法如下

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;

        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();

        BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);

        preProcessXml(root);
        parseBeanDefinitions(root, delegate);
        postProcessXml(root);
    }

首先获取了root节点用于以便再次将root作为参数继续BeanDefinition的注册,而delegate用于解析xml中定义的各种属性
比如

    public static final String SCOPE_ATTRIBUTE = "scope";

    public static final String SINGLETON_ATTRIBUTE = "singleton";

    public static final String LAZY_INIT_ATTRIBUTE = "lazy-init";

随后parseBeanDefinitions(root, delegate);开始解析bean的定义了

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(delegate.getNamespaceURI(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;
                    String namespaceUri = delegate.getNamespaceURI(ele);
                    if (delegate.isDefaultNamespace(namespaceUri)) {
                        //解析默认标签
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //解析自定义标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

spring中标签有两种,一类是默认的标签,如

id="test" class="com.spring.aop.test1.TestBean"/>

另一类是自定义标签如

<aop:aspectj-autoproxy/>

那么spring如何确认标签是自定义的还是默认的标签呢?spring依赖固定的命名空间http://www.springframework.org/schema/beans进行对比,如果一致就是默认否则就是自定义.对这两种标签的解析采用了不同的方式,将在接下来的文章中讲解

你可能感兴趣的:(Spring,spring,源码)