Spring IOC原理分析

IOC

IOC(Inversion of Control)控制反转:所谓的控制反转,就是把原先需要我们代码自己实现对象的创建和依赖,反转给容器来实现。那么必然Spring需要创建一个容器,同时需要创建一种描述对象与对象之间的依赖关系,这个描述就是Spring的配置文件。

假设我们自己设计Spring框架我们应该怎样考虑:

1.怎么表示对象与对象之间的关系

可以用 xml,properties 文件等语义化配置文件表示。

2.描述对象和对象之间关系的文件存放在哪里

可能是classpath、filesystem、URL网络资源、ServletContext等。

3.不同的配置文件对于对象的描述不一样应该如何统一

内部需要一个统一的关于对象的定义,所有外部描述都需要转化为统一的描述定义

4.如何对不同的配置文件解析

需要对不同的配置文件语法,采用不同的解析器

Spring核心容器

1.BeanFactory

spring Bean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多IOC容器的实现供用户选择和使用

其相关关系如下:

BeanFactory作为最顶层接口,定义了IOC容器的基本功能规范,它有三个重要的子类ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory都是对它本身的扩展,从类图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口

那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有它使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程时,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表化的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这三个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。最基本的 IOC 容器接口BeanFactory,来看一下它的源码(Spring源码自行下载):

public interface BeanFactory {

   //对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
   //如果需要得到工厂本身,需要转义
   String FACTORY_BEAN_PREFIX = "&";
  
   //根据bean的名字,获取在IOC容器中得到bean实例
   Object getBean(String name) throws BeansException;

   //根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
   <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

   Object getBean(String name, Object... args) throws BeansException;

   <T> T getBean(Class<T> requiredType) throws BeansException;

   <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

   //提供对bean的检索,看看是否在IOC容器有这个名字的bean
   boolean containsBean(String name);

   //根据bean名字得到bean实例,并同时判断这个bean是不是单例
   boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

   boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

   boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
  
   boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
   
//得到bean实例的Class类型
  @Nullable
   Class<?> getType(String name) throws NoSuchBeanDefinitionException;

//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
   String[] getAliases(String name);
}

 在BeanFactory 里只对 IOC 容器的基本行为作了定义,根本不关心你的 Bean 是如何定义怎样加载的。正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。

而要知道工厂如何产生对象的,我们需要看具体的IOC容器实现,Spring提供许多的IOC容器实现,比如GenericApplicationContext、ClassPathXmlApplicationContext等

2.BeanDefinition

SpringIOC 容器管理了我们定义的各种 Bean 对象及其相互的关系,Bean 对象在 Spring 实现中是以 BeanDefinition 来描述的,其继承体系如下: BeanDefinition里面记录了Bean的信息beanClassName、FactoryBeanNameLazyInit等等

3.BeanDefinitionReader

Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过BeanDefintionReader 来完成,最后看看 Spring 中 BeanDefintionReader 的类结构图:


 可以发现xml、properties等解析Bean的方法都是基于BeanDefintionReader

Web IOC 容器

我们从最熟悉的DispatcherServlet来看起,我们最先想到的是Servlet的init方法,因为DispatcherServlet是基于Servlet实现的所以我们要找到DispatcherServlet里的init方法。通过查找我们找到在DispatcherServlet的父类HttpServletBean找到init方法,一下是方法源码

    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                //定位资源
                BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                //加载配置信息
                ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                initBeanWrapper(bw);
                bw.setPropertyValues(pvs, true);
            }
            catch (BeansException ex) {
                if (logger.isErrorEnabled()) {
                    logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                }
                throw ex;
            }
        }

        // Let subclasses do whatever initialization they like.
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }    

可以看到在init()方法中真正完成容器初始化的方法是initServletBean在FrameworkServlet里实现,继续跟进我们可以发现initWebApplicationContext方法,继续跟进发现configureAndRefreshWebApplicationContext方法调用 refresh()方法,这个是真正启动 IOC 容器的入口。IOC 容器初始化以后,最后调用了DispatcherServlet 的 onRefresh()方法,在 onRefresh()方法中又是直接调用 initStrategies()方法初始化 SpringMVC 的九大组件:

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        //多文件上传组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);
        //初始化handlerMapping
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);
        //初始化视图转化器
        initViewResolvers(context);
        //初始化缓存组件
        initFlashMapManager(context);
    }

Xml IOC容器

IOC容器的初始化包括定位、加载和注册这三个基本过程。我们以ApplicationContext为例,ApplicationContext 系列容器也许是我们最熟悉的,因为 Web 项目中使用的 XmlWebApplicationContext 就属于这个继承体系,还有 ClasspathXmlApplicationContext等,其继承体系如下图所示:

ApplicationContext里是允许上下文嵌套的,通过保持一个父上下文可以维持一个上下文体系。可以在这个上下文体系里查找Bean,查找时首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring应用提供了一个共享的 Bean 定义环境。

1.寻找入口

通过一个使用比较多的类入手ClassPathXmlApplicationContext,我们会使用main方法来使用,至少我这样做过

ApplicationContext app = new ClassPathXmlApplicationContext("application.xml"); 

看一下其构造函数

public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

实际调用

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

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

还有 像 AnnotationConfigApplicationContext 、 FileSystemXmlApplicationContext 、XmlWebApplicationContext 等都继承自父容器 AbstractApplicationContext主要用到了装饰器模式
和策略模式,最终都是调用 refresh()方法。

2.获取配置路径

 ClassPathXmlApplicationContext构造函数可以看到首先调用父类super(parent)方法设置好Bean的资源加载器,然后再调用父类AbstractRefreshableConfigApplicationContext的setConfigLocations方法设置Bean的定位路径

通过查看源码发现SpringIOC 容器在初始化时将配置的 Bean 配置信息定位为 Spring 封装的 Resource

3.开始启动

Spring IOC 容器对 Bean 配置资源的载入是从 refresh()函数开始的,refresh()是一个模板方法,规定了IOC 容 器 的 启 动 流 程 , 有 些 逻 辑 要 交 给 其 子 类 去 实 现 。 它 对 Bean 配 置 资 源 进 行 载 入,ClassPathXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh()函数启动整个 IOC 容器对 Bean 定义的载入过程,现在我们来详细看看 refresh()中的逻辑处理:

    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从
            //子类的refreshBeanFactory()方法启动
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //为BeanFactory配置容器特性,例如类加载器、事件处理器等
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //为容器的某些子类指定特殊的BeanPost事件处理器
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //调用所有注册的BeanFactoryPostProcessor的Bean
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //为BeanFactory注册BeanPost事件处理器.
                //BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //初始化信息源,和国际化相关.
                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.
                //为事件传播器注册事件监听器.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //初始化所有剩余的单例Bean
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //初始化容器的生命周期事件处理器,并发布容器的生命周期事件
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                //销毁已创建的Bean
                destroyBeans();

                // Reset 'active' flag.
                //取消refresh操作,重置容器的同步标识.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

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

refresh()方法主要为 IOC 容器 Bean 的生命周期管理提供条件,Spring IOC 容器载入 Bean 配置信息从 其 子 类 容 器 的 refreshBeanFactory() 方 法 启 动 , 所 以 整 个 refresh() 中
“ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()”这句以后代码的都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动

refresh()方法的主要作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。它类似于对 IOC 容器的重启,在新建立好的容器中对容器进行初始化,对 Bean 配置资源进行载入。

4.创建容器

obtainFreshBeanFactory()方法调用子类容器的 refreshBeanFactory()方法,启动容器载入 Bean 配置信息的过程,代码如下

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法, 容器真正调用的是其子类 AbstractRefreshableApplicationContext 实现的 refreshBeanFactory()方法, 方法的源
码如下:

@Override
    protected final void refreshBeanFactory() throws BeansException {
        //如果已经有容器,销毁容器中的bean,关闭容器
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建IOC容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
            customizeBeanFactory(beanFactory);
            //调用载入Bean定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

在这个方法中,先判断 BeanFactory 是否存在,如果存在则先销毁 beans 并关闭 beanFactory,接着创建 DefaultListableBeanFactory,并调用 loadBeanDefinitions(beanFactory)装载 bean 定义。

5.载入配置路径

AbstractRefreshableApplicationContext 中只定义了抽象的 loadBeanDefinitions 方法,容器真正调用的是其子类 AbstractXmlApplicationContext 对该方法的实现,AbstractXmlApplicationContext的主要源码如下:

   @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        //创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean定义资源
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        //为Bean读取器设置Spring资源加载器,AbstractXmlApplicationContext的
        //祖先父类AbstractApplicationContext继承DefaultResourceLoader,因此,容器本身也是一个资源加载器
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //为Bean读取器设置SAX xml解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        //当Bean读取器读取Bean定义的Xml资源文件时,启用Xml的校验机制
        initBeanDefinitionReader(beanDefinitionReader);
        //Bean读取器真正实现加载的方法
        loadBeanDefinitions(beanDefinitionReader);
    }

    protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
        reader.setValidating(this.validating);
    }

    //Xml Bean读取器加载Bean定义资源
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //获取Bean定义资源的定位
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
            //的Bean定义资源
            reader.loadBeanDefinitions(configResources);
        }
        //如果子类中获取的Bean定义资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            //Xml Bean读取器调用其父类AbstractBeanDefinitionReader读取定位
            //的Bean定义资源
            reader.loadBeanDefinitions(configLocations);
        }
    }

以XmlBean 读取器的其中一种策略 XmlBeanDefinitionReader 为例。XmlBeanDefinitionReader 调用其父类AbstractBeanDefinitionReader的 reader.loadBeanDefinitions()方法读取Bean配置资源。由于我们使用 ClassPathXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值为 null,因此程序执行 reader.loadBeanDefinitions(configLocations)分支

6.分配路径处理策略

在 AbstractBeanDefinitionReader 的抽象父类 AbstractBeanDefinitionReader 中定义了载入过程。AbstractBeanDefinitionReader 的 loadBeanDefinitions()方法源码如下:

  @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }
    //重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>);方法
    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //获取在IoC容器初始化过程中设置的资源加载器
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
                //加载多个指定位置的Bean定义资源文件
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }
                return loadCount;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
            //加载单个指定位置的Bean定义资源文件
            Resource resource = resourceLoader.getResource(location);
            //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }
            return loadCount;
        }
    }

    //重载方法,调用loadBeanDefinitions(String);
    @Override
    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int counter = 0;
        for (String location : locations) {
            counter += loadBeanDefinitions(location);
        }
        return counter;
    }

AbstractRefreshableConfigApplicationContext 的 loadBeanDefinitions(Resource...resources) 方法实际上是调用 AbstractBeanDefinitionReader 的 loadBeanDefinitions()方法。从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions()方法源码分析可以看出该方法就做了两件事:首先,调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源。其次,真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions()方法。在loadBeanDefinitions()方法中调用了 AbstractApplicationContext 的getResources()方法,跟进去之后发现 getResources()方法其实定义在 ResourcePatternResolver 中,此时,我们有必要来看一下ResourcePatternResolver 的全类图:

从上面可以看到 ResourceLoader 与 ApplicationContext 的继承关系,可以看出其实际调用的是DefaultResourceLoader 中 的 getSource() 方 法 定 位 Resource , 因 为ClassPathXmlApplicationContext 本身就是 DefaultResourceLoader 的实现类,所以此时又回到了ClassPathXmlApplicationContext 中来。

7.解析配置文件路径

XmlBeanDefinitionReader 通 过 调 用 ClassPathXmlApplicationContext 的 父 类DefaultResourceLoader 的 getResource()方法获取要加载的资源,其源码如下

  @Override
    public Resource getResource(String location) {
        Assert.notNull(location, "Location must not be null");

        for (ProtocolResolver protocolResolver : this.protocolResolvers) {
            Resource resource = protocolResolver.resolve(location, this);
            if (resource != null) {
                return resource;
            }
        }
        //如果是类路径的方式,那需要使用ClassPathResource 来得到bean 文件的资源对象
        if (location.startsWith("/")) {
            return getResourceByPath(location);
        }
        else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
            return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
        }
        else {
            try {
                // Try to parse the location as a URL...
                // 如果是URL 方式,使用UrlResource 作为bean 文件的资源对象
                URL url = new URL(location);
                return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
            }
            catch (MalformedURLException ex) {
                // No URL -> resolve as resource path.
                //如果既不是classpath标识,又不是URL标识的Resource定位,则调用
                //容器本身的getResourceByPath方法获取Resource
                return getResourceByPath(location);
            }
        }
    }

    protected Resource getResourceByPath(String path) {
        return new ClassPathContextResource(path, getClassLoader());
    }

DefaultResourceLoader 提供了 getResourceByPath()方法的实现,就是为了处理既不是 classpath标识,又不是 URL 标识的 Resource 定位这种情况

在ClassPathResource 中完成了对整个路径的解析。这样,就可以从类路径上对 IOC 配置文件进行加载,当然我们可以按照这个逻辑从任何地方加载,在 Spring 中我们看到它提供的各种资源抽象,比如ClassPathResource、URLResource、FileSystemResource 等来供我们使用。上面我们看到的是定位Resource 的一个过程,而这只是加载过程的一部分。例如 FileSystemXmlApplication 容器就重写了getResourceByPath()方法:

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

8.开始读取配置内容

继续回到 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource …)方法看到代表 bean 文件的资源定义以后的载入过程

//这里是载入XML形式Bean定义资源文件方法
    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 {
            //将资源文件转为InputStream的IO流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //从InputStream中得到XML的解析源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //这里是具体的读取过程
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
                //关闭从Resource中得到的IO流
                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();
            }
        }
    }
  //从特定XML文件中实际载入Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      //将XML文件转换为DOM对象,解析过程由documentLoader实现
      Document doc = doLoadDocument(inputSource, resource);
      //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
  ...
}

通过源码分析,载入 Bean 配置信息的最后一步是将 Bean 配置信息转换为 Document 对象,该过程由documentLoader()方法实现 ,下面就是如何解析配置文件这边暂时不做分析了,把解析出的Bean以BeanDefinition形式存在,最后注册到IOC容器中,看下DefaultListableBeanFactory类registerBeanDefinition方法

//存储注册信息的BeanDefinition
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);   
     @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        //校验解析的BeanDefiniton
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.beanDefinitionMap.get(beanName);

        if (oldBeanDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        "': There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                //注册的过程中需要线程同步,以保证数据的一致性
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        //检查是否有同名的BeanDefinition已经在IOC容器中注册
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            //重置所有已经注册过的BeanDefinition的缓存
            resetBeanDefinition(beanName);
        }
    }

IOC 容器初始化小结
现在通过上面的代码,总结一下 IOC 容器初始化的基本步骤:
1、初始化的入口在容器实现中的 refresh()调用来完成。
2、对 Bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition(),
其中的大致过程如下:通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader是默认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等方式来定为资源位置。如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 Bean 定义的资源,也 就 是 说 Bean 定 义 文 件 时 通 过 抽 象 成 Resource 来 被 IOC 容 器 处 理 的 ,容 器 通 过BeanDefinitionReader 来 完 成 定 义 信 息 的 解 析 和 Bean 信 息 的 注 册 , 往 往 使 用 的 是XmlBeanDefinitionReader 来 解 析 Bean 的 XML 定 义 文 件 - 实 际 的 处 理 过 程 是 委 托 给BeanDefinitionParserDelegate 来完成的,从而得到 bean 的定义信息,这些信息在 Spring 中使用BeanDefinition对象来表示-这个名字可以让我们想到loadBeanDefinition(),registerBeanDefinition()这些相关方法。它们都是为处理 BeanDefinitin 服务的,容器解析得到 BeanDefinition 以后,需要把它在 IOC 容器中注册,这由 IOC 实现BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个 HashMap 来保存得到的 BeanDefinition 的过程。这个 HashMap 是 IOC 容器持有Bean 信息的场所,以后对 Bean 的操作都是围绕这个 HashMap 来实现的。然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 Spring IOC 的服务了,在使用 IOC容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在 ServletContext 中的框架实现

最后是Spring IOC 运行时序图

 

 

 

原文地址:https://www.cnblogs.com/gudazhi/p/11088413.html