Spring源码阅读(二)—IOC容器初始化

Spring源码阅读(二)—IOC容器初始化

IOC控制反转是指在传统面向对象的系统中,合作对象的创建和管理由具体的业务对象完成,而如果能够把控制权从具体的对象转交给平台或者框架中,可以极大的降低面向对象系统设计复杂性,这种对象依赖关系控制权的转移就成为控制反转.

IOC容器的初始化(Bean载入)和容器依赖注入是两个相对独立的过程,容器的初始化只是将XML配置文件中Bean的信息装载到承载类BeanDefinition中不处理具体的依赖注入.

个人主页:tuzhenyu’s page
原文地址:Spring源码阅读(二)—IOC容器初始化

初始化原始容器主要是指通过对XML配置文件进行解析,将解析处理出来的bean装载到容器中。初始化主要包括以下四步:

  • 读入XML资源文件

  • 从资源文件中获取Document对象

  • 解析Document创建BeanDefination实例

  • 注册BeanDefinations

(一) 容器的初始化入口

  • ApplicationContext实现容器的初始化,ApplicationContext实现容器的初始化是调用BeanFactory。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
  • XMLBeanFactory实现容器的初始化,XMLBeanFactory实现容器初始化的原理是调用DefaultListableBeanFactory类和

XmlBeanDefinitionReader类。


BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
  • DefaultListableBeanFactory实现容器的初始化

    • 读入XML配置文件,创建IOC配置资源。

    • 创建一个BeanFactory,这里使用DefaultListableBeanFactory进行实例化。

    • 创建一个载入BeanDefinition的读取器,绑定BeanFactory实力。

    • 将XML配置文件解析成一个一个Bean的承载类BeanDefinition,并注册进BeanFactory。


Resource resource = new ClassPathResource("applicationContext.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);

(二) 读入XML资源文件

  • 将Resource通过EncodedSource类进行封装,放回EncodedSource对象实例。目的是考虑到Resource随中可能存在编码要求的情况。



public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return this.loadBeanDefinitions(new EncodedResource(resource));
}
  • 将EncodedSource对象实例抽取InputStream封装进InputSource类对象中,调用doLoadBeanDefinitions()方法开始解析。



public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if(this.logger.isInfoEnabled()) {
        this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }

    Object currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
    if(currentResources == null) {
        currentResources = new HashSet(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }

    if(!((Set)currentResources).add(encodedResource)) {
        throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    } else {
        int var5;
        try {
            InputStream ex = encodedResource.getResource().getInputStream();

            try {
                InputSource inputSource = new InputSource(ex);
                if(encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }

                var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                ex.close();
            }
        } catch (IOException var15) {
            throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
        } finally {
            ((Set)currentResources).remove(encodedResource);
            if(((Set)currentResources).isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }

        }

        return var5;
    }
}

(三) 从资源文件中获取Document对象

  • 调用doLoadDocument()方法解析传入的XML配置文件资源,生成对应的Document实例,并根据返回的Document对象生成BeanDefinitons注册进BeanFactory。



protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        Document ex = this.doLoadDocument(inputSource, resource);
        return this.registerBeanDefinitions(ex, resource);
    } catch (BeanDefinitionStoreException var4) {
        throw var4;
    } catch (SAXParseException var5) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
    } catch (SAXException var6) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
    } catch (ParserConfigurationException var7) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
    } catch (IOException var8) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
    } catch (Throwable var9) {
        throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
    }
}
  • 通过DocumentLoader类实例的loadDocument()方法解析资源,主要分为一下两部:

    • 获取对XML文件的验证模式(DTD或XSD),通过XML配置文件头是否包含”DOCTYPE”字样区分验证模式。

    • 加载XML配置资源,通过SAX解析得到相应的Document对象。

    • EntityResolver的作用是项目本身就哭提供一个如何寻找DTD生命的方法。


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

(四) 从Document中获取Bean实例

  • 创建BeanDefinitionDocumentReader实例,利用实例解析Document,注册bean

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
    documentReader.setEnvironment(this.getEnvironment());
    int countBefore = this.getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
    return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
  • 解析出document的root节点,判断root节点的profile属性用来切换生产环境和开发环境,解析parseBeanDefinition

protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
    if(this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute("profile");
        if(StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
            if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }

    this.preProcessXml(root);
    this.parseBeanDefinitions(root, this.delegate);
    this.postProcessXml(root);
    this.delegate = parent;
}
  • 获取root标签下所有子标签列表,判断是否为默认标签或自定义标签,分别进行处理;

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)) {
                    this.parseDefaultElement(ele, delegate);
                } else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    } else {
        delegate.parseCustomElement(root);
    }

}
  • 默认标签的解析

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if(delegate.nodeNameEquals(ele, "import")) {
        this.importBeanDefinitionResource(ele);
    } else if(delegate.nodeNameEquals(ele, "alias")) {
        this.processAliasRegistration(ele);
    } else if(delegate.nodeNameEquals(ele, "bean")) {
        this.processBeanDefinition(ele, delegate);
    } else if(delegate.nodeNameEquals(ele, "beans")) {
        this.doRegisterBeanDefinitions(ele);
    }

}
  • bean标签的解析注册

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if(bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        try {
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
        } catch (BeanDefinitionStoreException var5) {
            this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5);
        }

        this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }

}

(五) 总结

IOC容器的初始化经过了资源加载,资源解析成Document,Document解析成一条条BeanDefinition,BeanDefinition注册到缓存BeanDefinitionMap中这四步.容器的初始化并未进行相应bean的依赖注入,只是将XML配置文件中的信息转换成一条条的bean的信息存储在BeanDefinition中,这其中也包含了bean的依赖信息.

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