美文网首页
Spring IoC容器源码分析(Spring技术内幕读书笔记)

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

作者: IOMan1987 | 来源:发表于2019-01-03 12:58 被阅读6次

    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相关的整个依赖链的注入也完成了 。

    相关文章

      网友评论

          本文标题:Spring IoC容器源码分析(Spring技术内幕读书笔记)

          本文链接:https://www.haomeiwen.com/subject/eijfrqtx.html