• Spring IOC源码是什么
  • 为什么学习Spring IOC源码
  • Spring IOC源码该怎么学

Spring IOC源码是什么

根据Spring IOC原理理解文章中了解了IOC以及DI的原理,以及最基本的Spring例子。
接下来我们来思考一下,Spring IOC源码究竟是什么?

Spring IOC核心是容器,bean是Spring中最核心的内容,因为Spring就像一个大水桶,而bean就相当于大水桶中的水,水桶脱离了水什么用处都没有。在这里可以找到答案了,Spring IOC源码就是IOC容器中bean的底层实现。

为什么学习Spring IOC源码

底层源码是一个看似无法逾越的鸿沟,特别是在遇到各种异常问题时,不明白底层实现原理,这个时候开始有点懵逼了,于是会做以下两件事情:
1.上百度,Google查询问题,根据网上的解决方案去无脑处理,运气好一点解决了,运气差一点就解决不了。
2.向同事或同学请教,别人解决了,你会被鄙视(即使别人不鄙视你,起码会自卑吧);否则,别人没有解决,你会鄙视别人。

如果你不想做以上两件事情,那么就学习源码吧。

Spring IOC源码该怎么学

我们来看看控制台输出的I am kitty背后spring究竟做了哪些事情。作为开发人员一般很熟悉applicationContext.xml,关于测试类TestAnimal有使用了FileSystemXmlApplicationContext,那么我们就从XML这里入手(XML只是其中一种)。

package com.feiniu.springframework.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestAnimal2 {
    public static void main(String[] args) {
        ApplicationContext context = new FileSystemXmlApplicationContext(  
                "applicationContext.xml");  
        Animal animal = (Animal) context.getBean("animal");  
        animal.say();
    }
}

控制台输出内容如下:

2018-04-11 10:32:41,601 [main] INFO  [org.springframework.context.support.FileSystemXmlApplicationContext org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:503)] - Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@5ce33b57: startup date [Wed Apr 11 10:32:41 GMT+08:00 2018]; root of context hierarchy
2018-04-11 10:32:41,729 [main] INFO  [org.springframework.beans.factory.xml.XmlBeanDefinitionReader org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:315)] - Loading XML bean definitions from file [D:\yupan\workspace\FreshCrm3\applicationContext.xml]
2018-04-11 10:32:42,284 [main] INFO  [org.springframework.beans.factory.support.DefaultListableBeanFactory org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:577)] - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@78d5524a: defining beans [animal]; root of factory hierarchy
I am kitty!

理清一下思路

关键就是第一段代码,现在我们使用XML方式进行探路,那么applicationContext.xml就是的突破口,Cat类是如何加载到IOC容器中?

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

applicationContext.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:task="http://www.springframework.org/schema/task" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd"
    default-autowire="byName">

    <bean id="animal" class="com.feiniu.springframework.test.Cat">  
        <property name="name" value="kitty" />  
    </bean>

</beans>

假设

我们做个假设:IOC容器就是一个HashMap,如何将Cat类存入HashMap中?

animal作为key
com.feiniu.springframework.test.Cat作为value

验证

控制台输出的I am kitty背后spring究竟做了哪些事情。作为开发人员一般很熟悉applicationContext.xml,关于测试类TestAnimal有使用了FileSystemXmlApplicationContext,那么我们就从这里入手,源码调用类图:

spring源码应该怎么看_XML

由此可以看出,接口BeanFactory就是Spring源码的入口。

BeanFactory: Spring的Bean容器最顶层的接口,定义了Ioc容器的基本规范。实现这个接口的IOC容器都会持有一些BeanDefinition和一个唯一的字符串形式的名字。
HierarchicalBeanFactory:继承BeanFactory并扩展使其支持层级结构。getParentBeanFactory()方法或者父级BeanFactory,containsLocalBean(String name)方法查看当前BeanFactory是否包含给定名字的Bean,不会递归父级查找。
ListableBeanFactory:同样扩展BeanFactory使其支持迭代Ioc容器持有的Bean对象。注意如果ListableBeanFactory同时也是HierarchicalBeanFactory,那么大多数情况下,只迭代当前Ioc容器持有的Bean对象,不会在体系结构中父级递归迭代。具体情况请看API说明。
ResourceLoader:Spring提供资源的根接口。在Spring Ioc中,资源被Resource引用,获得Resource对象,说明获得了资源的访问。Resource提供资源的抽象,具体资源可是从URL,classpath,file等地方获得。
ResourcePatternResolver:ResourcePatternResolver是对ResourceLoader的扩展,其支持模式匹配的资源。如:classpath*:表示匹配路径下所有的资源。
DefaultResourceLoader:ResourceLoader的默认实现,可以单独使用,也可以通过扩展使其支持特殊的资源,如:FileSystemResourceLoader,ClassPathXmlApplicationContext等。
ApplicationEventPublisher:封装事件发布,通知事件监听者此Application的事件。
MessageSource:处理Spring 中的消息,支持i18n和参数化消息。另外其子类ReloadableResourceBundleMessageSource支持不重启JVM刷新消息。
EnvironmentCapable:实现此接口的容器将支持上下文环境。在Spring Ioc容器中,都是支持上下文环境的。
ApplicationContext:从上图来看,ApplicationContext继承了上面描述的所有接口,因此ApplicationContext是一个接口集合,提供所继承接口的功能。另外,ApplicationContext在启动后是只读的,但是如果ApplicationContext实现类支持reload,也可以刷新这个ApplicationContext。
Lifecycle:对BeanFactory提供生命周期支持。另外其他任何对象都可以实现Lifecycle接口开支持开始/结束控制。注意Lifecycle接口只支持顶层对象,其他的Lifecycle将被忽略。
DisposableBean:DisposableBean提供了在销毁Ioc容器的时候释放资源。
ConfigurableApplicationContext:提供对Ioc容器的配置的支持。包括设置父级容器,设置上下文环境,刷新容器,注册关闭容器钩子等。
AbstractApplicationContext:AbstractApplicationContext是Ioc容器的抽象实现,这里实现了大部分的功能:消息,事件,刷新容器,生命周期等。AbstractApplicationContext采用模板方法模式,把一部分实现推迟到子类。
AbstractRefreshableApplicationContext:提供多线程同时刷新容器支持,每次刷新都会产生一个内部BeanFactory(DefaultListableBeanFactory)。另外,子类要实现loadBeanDefinitions方法来正确加载Bean定义。
Aware:Aware是个标记接口,实现这个接口的对象提供通知Spring容器功能。具体个通知动作来子类中定义。
BeanNameAware:Aware的子接口,当设置BeanName的时候,创建通知。
InitializingBean:这个接口作用是当Bean对象的属性都被设置完成或,可以立即做一些自定义的动作。令一个替代方案是设置init-method。
AbstractRefreshableConfigApplicationContext:提供对容器的一些特殊设置:setConfigLocation,setBeanName,setId等。
AbstractXmlApplicationContext:从XML读取Bean定义的容器,这个容器实现了loadBeanDefinitions方法,从XML资源中获得Bean定义。
FileSystemXmlApplicationContext:标准的从文件系统读XML的Bean定义容器。getResourceByPath方法返回文件系统资源。
说了这么多的BeanFactory,还有一个重量级的没有说:DefaultListableBeanFactory
DefaultListableBeanFactory包含了Ioc容器的重要内容,很多容器都会用的它。如AbstractApplicationContext.refersh()方法就会销毁内部的容器并重新创建一个DefaultListableBeanFactory作为起内部表示。DefaultListableBeanFactory则直接继承它成为从XML读取资源的Ioc容器。

起航FileSystemXmlApplicationContext构造方法

FileSystemXmlApplicationContext 构造方法

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

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

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

}

初始化applicationContext.xml文件路径,保存文件名称到变量configLocations

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
        implements BeanNameAware, InitializingBean {

    private String[] configLocations;

    public void setConfigLocations(String[] locations) {
        if (locations != null) {
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];
            for (int i = 0; i < locations.length; i++) {
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }
}

下面这段就是在解析文件名,学习一下Spring就怎么解析的

this.configLocations[i] = resolvePath(locations[i]).trim();

refresh默认状态为true,执行refresh()方法

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

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

看看refresh()方法做了哪些事情

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext, DisposableBean {

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            // 准备进行上下文刷新。
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            // 告诉子类刷新内部bean工厂,并进行XML文件读取
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            // 准备在这种情况下使用的bean工厂,对beanFactory进行各种功能填充
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                // 子类覆盖方法做额外的处理。
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                // 激活各种beanFactory处理器。
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                // 注册拦截bean创建bean处理器,这里只是注册,真正的调用是在getBean的时候。
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                // 为上下文初始化Message源,即不同语言的消息体,国际化处理。
                initMessageSource();

                // Initialize event multicaster for this context.
                // 初始化应用消息广播器。
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                // 留给子类来初始化其它的bean。
                onRefresh();

                // Check for listener beans and register them.
                // 在所有注册的bean中查找Listener bean,注册到消息广播器中。
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                // 初始化剩下的单实例(非惰性的)。
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                // 完成刷新过程:通知生命周期处理器lifecycleProcess刷新过程,同时发出ContextRefreshEvenet通知别人。
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                //销毁已经创建的单例以避免悬挂资源。
                destroyBeans();

                // Reset 'active' flag.
                //重置“活动”标志。
                cancelRefresh(ex);

                // Propagate exception to caller.
                //将异常传播给调用者。
                throw ex;
            }
        }
    }

}

Spring IOC refresh()方法——系统属性及环境变量的初始化以及验证 prepareRefresh(); Spring IOC refresh()方法——告诉子类刷新内部bean工厂 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); Spring IOC refresh()方法——准备在这种情况下使用的bean工厂 prepareBeanFactory(beanFactory);