Spring IoC容器源码分析(Spring技术内幕读书笔记)

2019-01-03  本文已影响6人  IOMan1987

1. 容器简介

在Spring容器设计中,BeanFactory实现容器的基本功能,ApplicationContext作为容器高级形态存在,在简单容器的基础上,增加了许多面向框架的特性。

Spring通过BeanDefinition来管理基于Spring应用中的各种对象以及它们之间的关系, 对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕BeanDefinition的处理来完成的。

在独立的程序中,一般使用ClassPathXmlApplicationContextFileSystemXmlApplicationContext启动Spring容器,这2个是ApplicationContext的具体实现类。

ApplicationContext表示IoC容器,负责实例化、配置以及组装Bean,通过配置数据获取(configuration metadata), 配置数据包括XML, Java注解, Java代码, 利用Java的反射功能实例化Bean并建立Bean与Bean之间的依赖关系

BeanFactory和ApplicationContext的区别

BeanFactory接口类是IoC设计的最基本的功能规范, ApplicationContext是复杂功能的衍生扩展

ApplicatonContext通过继承MessageSource、ResourceLoader、ApplicationEventPublisher接口,在BeanFactory简单容器的基础上添加了许多对高级容器的特性支持

所以我们一般是通过ApplicationContext启动IoC容器, ApplicationContext的getBean()方法就是BeanFactory接口方法

2. IoC容器的设计原理

2.1 接口

IoC容器是由一系列的接口组成的,以BeanFactoryApplicationContext为核心,BeanFactory是最基本的接口, 在ApplicationContext的设计中,它集成了BeanFactory接口体系中的ListableBeanFactoryAutowireCapableBeanFactoryHierarchicalBeanFactory等BeanFactory接口,具备BeanFactory IoC容器的基本功能,并且,通过集成MessageSourceResourceLoaderApplicationEventPublisher等接口赋予了IoC容器更高级的特性。

Spring IoC容器的设计接口图

BeanFactory接口线

从接口BeanFactoryHierarchicalBeanFactory,再到ConfigurableBeanFactory是一条主要的BeanFactory设计路径,定义了IoC容器的基本规范。而HierarchicalBeanFactory增加了getParentBeanFactory(),使BeanFactory具备了双亲IoC容器的管理功能,在接下来的ConfigurableBeanFactory接口中,定义了一些对BeanFactory的配置功能

ApplicationContext接口线

第二条接口设计主线,以ApplicationContext接口上下文为设计核心,从 BeanFactoryListableBeanFactory, 再到ApplicationContext, 再到WebApplicationContext或者ConfigurableApplicationContext. 在这个体系中, ListableBeanFactory接口和HierarchicalBeanFactory两个接口, 连接BeanFactoryApplicationContext

ListableBeanFactory接口中,细化了BeanFactory的接口功能,对于ApplicationContext接口,通过继承MessageSourceResourceLoaderApplicationEventPublisher等接口,在BeanFactory的基础上添加了许多对高级容器的特性支持。

2.2 BeanFactory容器的设计原理

DefaultListableBeanFactory 是 IoC容器最常用的具体实现类,而XmlBeanFactory继承了DefaultListableBeanFactory, XmlBeanFactory扩展了读取Xml文件的功能,也就是说XmlBeanFactory是一个可以读取XML文件方式定义的BeanDefinition的IoC容器

XMLBeanFactory中,初始化了一个XmlBeanDefinitionReader对象,有了Reader对象,那些以XML方式定义的BeanDefinition就有了处理的地方, 实际上Xml信息的处理是由XmlBeanDefinitionReader完成的。

XMLBeanFactory构造函数中传入Resource对象,Resource是Spring用来封装I/O操作的类。比如XML文件形式的Resource可以使用ClassPathResource实现

所以,在整个BeanFactory容器的实现过程中,DefaultListableBeanFactory是一个很重要的IoC实现,在ApplicationContext的实现中,原理和XMLBeanFactory一样,也是通过持有或者扩展DefaultListableBeanFactory来获得基本的IoC容器的功能。

编程实现

ClassPathResource res = new ClasspathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
  1. 创建IoC配置文件的抽象资源,包含了BeanDefinition的定义信息
  2. 创建一个BeanFactory,这里使用DefaultListableBeanFactory
  3. 创建一个载入BeanDefinition的读取器,这里使用XmlBeanDefinitionReader来加载XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory
  4. 加载定义好的资源,解析BeanDefinition,完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了

XmlBeanFactory在Spring 3.1之后被废弃,一般的,我们使用ApplicationContext启动IoC容器

2.3 ApplicationContext容器的设计原理

ApplicationContextBeanFactory的基础上添加了附加的功能,使得IoC容器的功能更加丰富,所以一般建议在开发时使用ApplicationContext作为IoC容器的基本形式,以常见的FileSystemXmlApplicationContext进行说明。

FileSystemXmlApplicationContext作为具体的应用上下文,实现和它自身设计相关的两个功能。

一个是直接使用FileSystemXmlApplicationContext,支持实例化的这个应用上下文,同时启动IoC容器的refresh()过程,这个refresh()过程涉及一系列复杂的操作,将在IoC容器的初始化过程中说明

另一个是与FileSystemXmlApplicationContext设计具体相关的功能,与从文件系统中加载XML的Bean定位资源相关,相关代码:

protected Resource getResourceByPath(String path) {
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    return new FileSystemResource(path);
}

3. IoC容器的初始化过程

ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml");

我们一般通过上面代码启动Spring IoC容器, 下面我们根据这行代码,进行分析IoC容器的初始化过程, Spring IoC容器的启动包括BeanDefinition的Resource定位、载入和注册三个基本过程。

继承关系

3.1 Resource定位

Resource定位指的是BeanDefinition的定位过程,这里用的IoC容器是ListableBeanFactory,DefaultListableBeanFactory并不能直接使用Resource, 必须通过BeanDefinitionReader来处理,在ApplicationContext中,Spring为我们提供了一系列加载不同Resource的Reader实现,DefaultListableBeanFactory是纯粹的容器,我们需要为其配置特定的Reader才能完成这些功能

image-20181228210454211-6002294.png
FileSystemXmlApplicationContext类的继承关系

AbstractXmlApplicationContext

专门针对XML文件进行处理,重写了loadBeanDefinitions()方法,实例化了XmlBeanDefinitionReader,赋予读取了Xml配置文件的能力

AbstractRefreshableConfigApplicationContext

为ClassPathXmlApplicationContext、FileSystemXmlApplicationContext以及XmlWebApplicationContext提供通用的配置路径处理能力

AbstractRefreshableApplicationContext

最最重要的ApplicationContext的实现类,创建DefaultListableBeanFactory容器,并且提供了loadBeanDefinitions方法, 赋予了其子类AbstractXmlApplicationContext 等实现载入BeanDefinition的能力

AbstractApplicationContext

ApplicationContext接口的抽象实现类,是ApplicationContext的超级大打手,使用了模板方法设计模式,自动注册了BeanPostProcessors、ApplicationListeners以及MessageSource等高级功能,其中的refresh()方法就是FileSystemXmlApplicationContext等具体ApplicationContext实现类构造函数的启动入口, 老大终于现真身了

DefaultResourceLoader

ResourceLoader接口的实现类,会根据不同的配置文件类型,提供不同的Resource,比如UrlResource和ClassPathResource等,该类的getResource()方法就是真正调用FileSystemXmlApplicationContext的getResourceByPath()的地方。

源码剖析

通过上面几个类层级关系,大致清楚了FileSystemXmlApplicationContext的实现关系,下面通过分析源代码,来剖析实现过程。

new FileSystemXmlApplicationContext(String configLocation)

public FileSystemXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

refresh()方法是整个容器的触发点

@Override
protected Resource getResourceByPath(String path) {
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    return new FileSystemResource(path);
}

在这个方法中,阐明了用何种Resource进行定位,这里通过FileSystemResource方式载入资源.

getResourceByPath()是在DefaultResourceLoader中定义的,通过上述继承图可以发现,DefaultResourceLoader是AbstractApplicationContext的基类, 而FileSystemXmlApplicationContext继承AbstractApplicationContext, 所以FileSystemXmlApplicationContext本质是通过DefaultResourceLoader进行资源定位的

最终该方法会被XmlBeanDefinitionReader的loadBeanDefinitions方法触发 InputStream inputStream = encodedResource.getResource().getInputStream();, 在DefaultResourceLoader的getResource()方法中被调用

在XmlWebApplicationContext和ClassPathXmlApplicationContext等其他ApplicationContext中,都会定义自己的Resource, 并且最终由基类DefaultResourceLoader的getResource()方法执行

AbstractApplicationContext.refresh()

refresh是一个很重要的方法,是IoC容器的入口,refresh详细的描述了整个ApplicationContext初始化的过程,比如BeanFactory的更新,MessageSource和PostProcessor的注册等等,这个执行过程为Bean的生命周期提供了条件。

refresh中的执行步骤如下:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                ...
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

AbstractApplicationContext.obtainFreshBeanFactory()

这里执行AbstractRefreshableApplicationContext的refreshBeanFactory方法,准备刷新容器

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        return getBeanFactory();
    }

AbstractRefreshableApplicationContext.refreshBeanFactory()

在创建容器前,如果容器已经存在,则需要先销毁和关闭,保证在refresh之后的容器的新的容器,所以refresh更像是重启的重启. 这里真正的创建了容器DefaultListableBeanFactory,然后当容器建立之后,就开始进行最重要的BeanDefinitions信息的载入

protected final void refreshBeanFactory() throws BeansException {
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

AbstractXmlApplicationContext.loadBeanDefinitions()

在AbstractRefreshableApplicationContext中调用的loadBeanDefinitions是抽象方法,真正执行的地方在这里,在AbstractXmlApplicationContext的loadBeanDefinitions中,初始化了读取器XmlBeanDefinitionReader,配置基本信息,使其具备了读取XML配置的能力

// Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

当XmlBeanDefinitionReader组装完毕后,就准备好大显身手了

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }

AbstractBeanDefinitionReader.loadBeanDefinitions(String location)

AbstractBeanDefinitionReader是XmlBeanDefinitionReader的父类,这里为载入BeanDefinitions做好了准备

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int count = 0;
        for (String location : locations) {
            count += loadBeanDefinitions(location);
        }
        return count;
    }

XmlBeanDefinitionReader.loadBeanDefinitions(Resource resource)

XMLBeanDefinitionReader是真正实现解析XML配置的地方,在loadBeanDefinitions方法中,拿到代表XML文件的Resource,真正打开了XML文件流,拿到Document对象,进行了BeanDefinitions的注册.

这里实际是通过DefaultResourceLoader调用了FileSystemApplicationContext的getResourceByPath方法拿到FileSystemResource进行XML资源的获取。

...
   InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                inputStream.close();
            }
    
...

doLoadBeanDefinitions方法

Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);

registerBeanDefinitions方法

BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

int countBefore = getRegistry().getBeanDefinitionCount();

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

return getRegistry().getBeanDefinitionCount() - countBefore;

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()

DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的实现类,真正的Document文档语义解析是委托给BeanDefinitionParserDelegate进行解析,该类包含了对各种Spring Bean定义规则的处理, 其处理的结果由BeanDefinitionHolder持有,BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition使用相关的信息,比如Bean的名字,别名名称等

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)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

这样经过逐层的解析,在XML文件中定义的BeanDefinition就被载入到了IoC容器中,并在容器中建立了数据映射。这些数据结构以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作,但重要的依赖注入此时还未发生,现在IoC容器中还只是存着静态的配置信息。

3.2 BeanDefinition在IoC容器中的注册

此时IoC容器还不能直接使用,需要在IoC容器中对这些BeanDefinition进行注册,具体是指在DefaultListableBeanFactory中的HashMap存储这些BeanDefinition信息

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

BeanDefinition注册的动作是在前文的DefaultBeanDefinitionDocumentReader.processBeanDefinition中执行,具体实现在BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry), 其调用了DefaultListableBeanFactory的registerBeanDefinition方法.

BeanDefinition注册完成后,就已经可以被IoC容器所使用了,他们都被存在DefaultListableBeanFactory的beanDefinitionMap中,可以被检索和使用。

3.3 IoC容器的依赖注入

DefaultListableBeanFactory.getBean()和AbstractBeanFactory.getBean()

依赖注入的入口,其具体实现是在DefaultListableBeanFactory的基类AbstractBeanFactory的getBean方法中具体的实现的,之后会调用createBean()

AbstractAutowireCapableBeanFactory.createBean()

createBean不但生成了需要的Bean,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等

AbstractAutowireCapableBeanFactory.createBeanInstance()

在createBeanInstance中生成了Bean所包含的Java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些都由相关的BeanDefinition来指定的。

如果未指定生成策略,默认有2种方式,一种是JVM的反射功能,一种是通过cglib来生成,SimpleInstantiationStrategy是生成对象的默认处理类

AbstractAutowireCapableBeanFactory.populateBean()

实例化后,会进行property的配置,通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中. 在BeanDefinitionValueResolver类进行具体的解析。 在这里会触发相关依赖Bean的递归实现,在resolveValueIfNecessary方法中进行解析处理。

依赖注入的发生是在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的。

一个递归是在上下文体系中查找需要的 Bean和创建Bean的递归调用, 另一个递归是在依赖注入时, 通过递归调用容器的getBean方法, 得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入 。在对Bean的属性进行依赖注入时 ,解析的过程也是一个递归的过程 。这样 ,根据依赖关系 ,一层一层地完成Bean的创建和注入 ,直到最后完成当前Bean的创建 。有了这个顶层Bean的创建和对它的属性依赖注入的完成 ,意味着和当前Bean相关的整个依赖链的注入也完成了 。

上一篇下一篇

猜你喜欢

热点阅读