Spring之容器的实现

来源:互联网 发布:mac查看内存占用 编辑:程序博客网 时间:2024/06/15 19:49

对于经常使用spring框架的同学,对于下面的这段代码肯定不会陌生

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

(1)读取配置文件applicationContext
(2)找到配置文件中定义的配置并实例化

以上是Spring实现容器的基础,虽然只有短短的一行,但是里面却包含了复杂的逻辑。为了分析里面包含的逻辑,我们可以使用他的父类BeanFactory来分析。下面代码是Spring比较原始的实现容器的方法。

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

代码实现的逻辑为:

1.使用ClassPathResource将beanFactory.xml封装成Resource
2.完成XmlBeanFactory的初始化

ClassPathResource

在 Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而 URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring对其内部资源,使用了自己的抽象结构:Resource接口来封装。而 ClassPathResource实现类即是对Resource的实现。而Resource实现了InputStreamSource。

public interface InputStreamSource {    InputStream getInputStream() throws IOException;}public interface Resource extends InputStreamSource {    boolean exists();    boolean isReadable();    boolean isOpen();    URL getURL() throws IOException;    URI getURI() throws IOException;    File getFile() throws IOException;    long contentLength() throws IOException;    long lastModified() throws IOException;    Resource createRelative(String relativePath) throws IOException;    String getFilename();    String getDescription();}

不同的Resource实现代表了不同来源的资源,对于来自ClassPath的资源就是用ClassPathResource来实现。

XmlBeanDefinitionReader

将配置文件封装好以后就需要对其读取,这个工作由XmlBeanDefinitionReader来完成。XmlBeanDefinitionReader中由loadBeanDefinitions方法实现读取的功能。loadBeanDefinitions方法的源代码如下:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {        //loadBeanDefinitions的具体实现,而EncodedResource主要用于对资源文件的处理        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());        }//通过属性来记录已经加载的资源        。。。。。。。。。。        // 调用DefaultResourceLoader的getResources方法完成具体的Resource定位         try {                //从EncodedResource中获取已经封装的Resource对象并再次从Resource中获取inputStream                 InputStream inputStream = encodedResource.getResource().getInputStream();                try {                        InputSource inputSource = new InputSource(inputStream);                        if (encodedResource.getEncoding() != null) {                                inputSource.setEncoding(encodedResource.getEncoding());                        }                         //真正的实现核心                        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());                   }                。。。。。。。。。。。。。}

对于上述源码,只需要知道实现逻辑:
(1)对于resource做一次封装,为了考虑到resource可能存在的编码问题,我们使用了new EncodedResource(resource)来实现。
(2)准备获得InputSource对象。
(3)将封装好的resource和InputSource传递给真正的核心部分:doLoadBeanDefinitions(inputSource, encodedResource.getResource());

doLoadBeanDefinitions

我们看一看doLoadBeanDefinitions(inputSource, encodedResource.getResource())中的实现方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)            throws BeanDefinitionStoreException {    //这里略去了冗余的代码,值需要注意三个方法    //获得验证模式    int validationMode = getValidationModeForResource(Resource resource)    //获得document    Document document = loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware)    //注册Bean信息    registerBeanDefinitions(Document doc, XmlReaderContext readerContext)            }

doLoadBeanDefinitions只做了三件事:
1.获得验证模式
2.获得document
3.注册Bean信息

获得验证模式

验证模式就两种,DTD和XSD。如果xml文件中有则表示使用DTD验证模式,否则使用XSD。源代码如下,比较简单。

protected int getValidationModeForResource(Resource resource) {        int validationModeToUse = getValidationMode();        //如果手动指定了验证模式则使用指定的验证模式        if (validationModeToUse != VALIDATION_AUTO) {                return validationModeToUse;        }        //如果没有指定,则自动检测        int detectedMode = detectValidationMode(resource);    //自动检测主要是在detectValidationMode(Resource resource)完成的        if (detectedMode != VALIDATION_AUTO) {                return detectedMode;        }        return VALIDATION_XSD;}

获得Document

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);}

逻辑:
1.创建DocumentBuilderFactory
2.通过DocumentBuilderFactory创建DocumentBuilder
3.DocumentBuilder解析inputSource返回一个Document对象。

registerBeanDefinitions

获得了XML文档文件的Document以后,就会执行BeanDefinitions的解析和注册。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {        // 得到BeanDefinitionDocumentReader来对XML的BeanDefinition进行解析         BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();        int countBefore = getRegistry().getBeanDefinitionCount();        // 具体的解析过程在BeanDefinitionDocumentReader的registerBeanDefinitions方法中完成        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));        return getRegistry().getBeanDefinitionCount() - countBefore;    }

registerBeanDefinitions对前一步获得的Document进行了处理,实现注册和解析由方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))实现。

documentReader中的registerBeanDefinitions方法如下:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {        this.readerContext = readerContext;        logger.debug("Loading bean definitions");        Element root = doc.getDocumentElement();    // 获得Document的根元素        //核心逻辑        doRegisterBeanDefinitions(root);}

真正的实现XML文件解析和加载的核心部分——doRegisterBeanDefinitions方法

protected void doRegisterBeanDefinitions(Element root) {        BeanDefinitionParserDelegate parent = this.delegate;        this.delegate = createDelegate(getReaderContext(), root, parent);        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);    // 解析Bean定义之前, 增强解析过程的可扩展性         parseBeanDefinitions(root, this.delegate);    // 从Document的根元素开始进行Bean定义的Document对象          postProcessXml(root);    // 解析Bean定义之前, 增强解析过程的可扩展性          this.delegate = parent;}

核心代码逻辑如下:
1.对profile属性的处理。不同的profile属性可以理解为:代表了xml中beans的不同namespace,一个namespace代表一套的配置方案。

2.preProcessXml方法。在解析之前,我们可以根据需要实现一些逻辑,需要我们自己实现。

3.parseBeanDefinitions方法,对xml配置文件的读取和解析。

4.postProcessXml,在解析完成之后,我们可以根据需要实现一些逻辑,需要我们自己实现。

总结

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

1.beanFactory.xml→ClassPathResource
2.使用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)读取Resource。

loadBeanDefinitions(resource)的实现逻辑:
2.1.封装资源,new EncodeResource(resource);
2.2.获得输入流,并构造inputsource。
2.3.调用doLoadBeanDefinitions(inputsource,resource)。

doLoadBeanDefinitions(inputsource,resource)的实现:
2.3.1.获得XML验证模式:
方法:getValidationModeForResource
2.3.2.加载XML文件,获得Document类。
方法:loadDocument
2.3.3.根据Document获得Bean。
方法:registerBeanDefinitions(document,resource);

registerBeanDefinitions(document,resource)中的doRegisterBeanDefinitions方法是真正的核心实现。

doRegisterBeanDefinitions实现逻辑:
1.处理profile
2.preProcessXml
3.parseBeanDefinitions方法,对xml配置文件的读取和解析
4.postProcessXml