Spring源码学习笔记(1)——容器的基本实现

时间:2022-07-24
本文章向大家介绍Spring源码学习笔记(1)——容器的基本实现,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Spring源码学习笔记(1)——容器的基本实现

一. 实例代码

Resource resource = new ClassPathResource("applicationContext.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
Object bean = beanFactory.getBean("beanName");

以上是很简单的一段代码,只完成了3个Spring的基本操作:

  1. 读取Spring配置文件
  2. 根据配置文件创建BeanFactory,加载Bean
  3. 从BeanFactory中获取Bean的实例

上述代码完成了Spring容器的基本功能,看似简单,但是背后却封装了复杂的逻辑。下面我们结合源码看下Spring到底做了哪些处理。

一. 资源的封装——Resource接口

  1. Resource接口抽象了所有Spring内部使用的底层资源,如File、Classpath下的资源和ByteArray等。Resource接口继承了InputStreamSource接口,InputStreamSource接口只有一个方法:
public interface InputStreamSource {
   //返回一个新的InputStream对象
InputStream getInputStream() throws IOException;

}
  1. Resource接口提供了一些资源基础属性的相关方法。
public interface Resource extends InputStreamSource {

boolean exists();

default boolean isReadable() {
    return true;
}

default boolean isOpen() {
    return false;
}

default boolean isFile() {
    return false;
}

URL getURL() throws IOException;

URI getURI() throws IOException;

File getFile() throws IOException;

default ReadableByteChannel readableChannel() throws IOException {
    return Channels.newChannel(getInputStream());
}

long contentLength() throws IOException;

long lastModified() throws IOException;

Resource createRelative(String relativePath) throws IOException;

@Nullable
String getFilename();

String getDescription();

}
  1. 对于不同来源的资源,Resource有不同类型的实现类,如FileSystemResource、ClassPathResource、UrlResource等。
  2. 通常,Spring会将配置文件封装成一个Resource对象,后续属性的解析和Bean的加载就交给BeanDefinitionReader去处理。

二. 资源的解析——BeanDefinitionReader

  1. 当Spring将配置文件封装成了Resource之后,就交给BeanDefinitionReader去对其进行加载和解析。 BeanDefinitionReader定义了解析配置文件的相关方法。
public interface BeanDefinitionReader {

BeanDefinitionRegistry getRegistry();

@Nullable
ResourceLoader getResourceLoader();

@Nullable
ClassLoader getBeanClassLoader();

BeanNameGenerator getBeanNameGenerator();

int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

}

  1. Spring配置文件可以有不同的描述方式,如xml、properties等,根据不同的描述类型,有不同的实现类,如XmlBeanDefinitionReader、PropertiesBeanDefinitionReader等。

三. Bean的加载

Spring的Bean是何时被加载的呢?看示例代码:

new XmlBeanFactory(resource);

创建了XmlBeanFactory实例,并传入Resource对象,Resource就在这里被加载解析:

public class XmlBeanFactory extends DefaultListableBeanFactory {
    //XmlBeanFactory内部维护了一个XmlBeanDefinitionReader对象,用来解析以Xml方式描述的配置文件
    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        //构造XmlBeanFactory后,即调用XmlBeanDefinitionReader的方法解析配置文件
        this.reader.loadBeanDefinitions(resource);
    }

}

XmlBeanFactory调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法解析配置文件,加载Bean定义。具体如下:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException{
    //对Resource进行编码包装
    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());
        }

        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //从Resource中获取InputStream并构造InputSource
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //调用doLoadBeanDefinitions()进行BeanDefinition的解析
                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();
            }
        }
    }

可以看到,loadBeanDefinitions()方法只是做了一些准备工作,核心的加载逻辑是在doLoadBeanDefinitions()方法中的:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
    try {
        //将封装了Spring配置文件的Resource对象解析成Document文档对象
        Document doc = doLoadDocument(inputSource, resource);

        //向Spring容器中注册BeanDefinition
        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);
    }
}

不考虑异常处理,doLoadBeanDefinitions()方法其实只完成了两件事:

  1. 将资源解析成Document对象;
  2. 根据解析好的Document对象,向容器中注册BeanDefinition

Document是w3c规范定义的文档对象,doLoadDocument(inputSource, resource)方法的作用就是将Spring的配置文件Resource对象解析成Document对象,具体的逻辑涉及到dom解析相关处理,这里就不再展开了。

下面重点关注registerBeanDefinitions方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //创建DefaultBeanDefinitionDocumentReader实例,用来解析Document文件
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

    //本次注册BeanDefinition前,容器中BeanDefinition的数量
    int countBefore = getRegistry().getBeanDefinitionCount();
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

    //返回本次成功注册的BeanDefinition的数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

注册BeanDefinition的核心逻辑在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions()方法中:

protected void doRegisterBeanDefinitions(Element root) {
    //创建BeanDefinitionParserDelegate代理对象专门处理解析
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    //处理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)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    //解析预处理,模板方法
    preProcessXml(root);

    //实际处理解析
    parseBeanDefinitions(root, this.delegate);

    //解析后处理,模板方法
    postProcessXml(root);

    this.delegate = parent;
}

最后,解析和注册BeanDefinition的处理在parseBeanDefinitions()方法中

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    //解析<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)) {
                    //解析<bean>标签
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }

    //处理自定义标签
    else {
        delegate.parseCustomElement(root);
    }
}