小编教您Spring源码分析之IoC容器初始化

作为一个java程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么初始化IoC容器的?

image-20181204223850227

注:阅读本文时一定要在IDE进行跳转

我们都是知道Spring为企业级应用提供了丰富的功能,而这些功能底层都依赖于底层最核心的两种特性IOC和AOP。

IOC实现里主要包括两部分,一个是IOC容器初始化,另外一个是依赖注入,由于两部分是相对独立的部分,所以分成不同文章讲解,本篇主要讲述IOC容器的初始化

一、IoC的概念

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

上面这个概念是来自维基百科,在Expert Spring MVC and Web FlowExpert One-on-One J2EE without EJB等书中都也是将依赖注入看作是IoC的一种方式。不过有些地方会把IoC和DI看成一个概念(例如Spring in Action、Spring揭密等书),不过没关系,不影响我们理解就可以。

白话版

A类的正常的运行需要B类

没有IoC之前我们在使用A类的某个方法时,总会依赖于B类的一些功能,这样我们就要去new个B对象,有时还要考虑什么时候销毁,什么时候使用单例模式等等,类少了还好管理,这要是多起来真是再聪明的人怕也是要十个头九个大了, 而且A、B之间的依赖关系使各代码紧密耦合,一旦B类的出现问题,或者某天干脆不用B了用C,那是不是A类里的new B()全得换成new C()?想象都觉得累...

有了IoC之后,对象创建都交给第三方容器处理,A中的B通过注入来完成,B出问题了,或者需要换成C了,只要把注入的B换成C就可以(现实开发中B、C可以实现相同的接口解决~所以啊,Spring是面向接口编程鸭)。

Tips

  1. Expert One-on-One J2EE without EJB这本书是spring爸爸Rod Johnson写的,进入Spring的BeanFactory类里面看看作者就是他,哈哈!
  2. 浅谈控制反转与依赖注入:这是我看过最好的一篇对控制反转的解释,强烈推荐!

二、IoC容器初始化

预备内容

本节只讲解IoC容器的初始化,其中包括创建容器和将bean装入到容器中,下面这三件事是该部分的核心:

BeanDefinition的Resource定位
BeanDefinition的载入和解析
BeanDefinition在容器中的注册

因为Spring的IoC容器实现太复杂了,各种类之间的调用很容易就让我们陷入到细节之中,结果就走的太远忘记了为啥要出发了?,本文主要将述容器初始化时最主要的三件事。

先了解几个概念:

BeanFactory:这是IOC容器的接口定义,提供了IoC最基本的功能,如果说容器是个汽车工厂,那么这个结构就规定了汽车工厂最基本的功能,能储存零件,能组装汽车。

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">interface</span> <span style="color:#a31515">BeanFactory</span> {
    <span style="color:green">/**
     * 使用容器获取bean时,添加转义自符&可以获取到FactoryBean本身而吧是Factory产生的对象
     */</span>
    String FACTORY_BEAN_PREFIX = <span style="color:#a31515">"&"</span>;
    <span style="color:green">/**
     * 通过bean的名字来获取bean
     */</span>
    Object <span style="color:#a31515">getBean</span>(String name) <span style="color:#0000ff">throws</span> BeansException;
    <span style="color:green">/**
     * 通过bean的类型和类型来获取bean
     */</span>
    <T> T <span style="color:#a31515">getBean</span>(String name, Class<T> requiredType) <span style="color:#0000ff">throws</span> BeansException;
    <span style="color:green">/**
     * 通过bean的类型来获取bean
     */</span>
    <T> T <span style="color:#a31515">getBean</span>(Class<T> requiredType) <span style="color:#0000ff">throws</span> BeansException;
    <span style="color:green">/**
     * 通过名字和参数来获取bean
     */</span>
    Object <span style="color:#a31515">getBean</span>(String name, Object... args) <span style="color:#0000ff">throws</span> BeansException;
    <span style="color:green">/**
     * 是否包含名字为name的bean
     */</span>
    <span style="color:#0000ff">boolean</span> <span style="color:#a31515">containsBean</span>(String name);
    <span style="color:green">/**
     * 是否单例
     */</span>
    <span style="color:#0000ff">boolean</span> <span style="color:#a31515">isSingleton</span>(String name) <span style="color:#0000ff">throws</span> NoSuchBeanDefinitionException;
    <span style="color:green">/**
     * 是否原型
     */</span>
    <span style="color:#0000ff">boolean</span> <span style="color:#a31515">isPrototype</span>(String name) <span style="color:#0000ff">throws</span> NoSuchBeanDefinitionException;
    <span style="color:green">/**
     * 名字为name的bean是否是targetType类型
     */</span>
    <span style="color:#0000ff">boolean</span> <span style="color:#a31515">isTypeMatch</span>(String name, Class<?> targetType) <span style="color:#0000ff">throws</span> NoSuchBeanDefinitionException;
    <span style="color:green">/**
     * 获取名字为name的bean类型
     */</span>
    Class<?> getType(String name) <span style="color:#0000ff">throws</span> NoSuchBeanDefinitionException;
    <span style="color:green">/**
     * 获取名字为name的bean的别名字集合
     */</span>
    String[] getAliases(String name);

}</code></span>

ApplicationContext:升级版汽车厂,除了上面的功能,还提供很多人性化的服务,继承了 MessageSource,ResourceLoader,ApplicationEventPublisher等等接口,在BeanFactory 简单IoC容器的基础上添加了许多对高级容器的支持。

image-20181201171729612

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">interface</span> <span style="color:#a31515">ApplicationContext</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">EnvironmentCapable</span>,<span style="color:#a31515">ListableBeanFactory</span>,<span style="color:#a31515">HierarchicalBeanFactory</span>,<span style="color:#a31515">MessageSource</span>,<span style="color:#a31515">ApplicationEventPublisher</span>,<span style="color:#a31515">ResourcePatternResolver</span> {
    <span style="color:green">/**
     * 返回该上下文的id(unique)
     */</span>
    String <span style="color:#a31515">getId</span>();

    <span style="color:green">/**
     *  返回上下文所属应用的名字
     */</span>
    String <span style="color:#a31515">getApplicationName</span>();

    <span style="color:green">/**
     * 返回这个上下文友好的名字
     */</span>
    String <span style="color:#a31515">getDisplayName</span>();

    <span style="color:green">/**
     * 返回上下文首次加载的时间
     */</span>
    <span style="color:#0000ff">long</span> <span style="color:#a31515">getStartupDate</span>();

    <span style="color:green">/**
     * 返回父类上下文
     */</span>
    ApplicationContext <span style="color:#a31515">getParent</span>();

    <span style="color:green">/**
     * 功能性的暴露自动装配的工厂,并不常用
     */</span>
    AutowireCapableBeanFactory <span style="color:#a31515">getAutowireCapableBeanFactory</span>() <span style="color:#0000ff">throws</span> IllegalStateException;
}
</code></span>

这里面的方法也不多,主要的方法都在继承的接口里

BeanDifinition:储存 Spring中 Bean的信息,包括各属性名,类名,是否单例等,抽象了我们对 Bean的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对控制反转模式中管理的对象依赖关系的数据抽象。

接下来正式进入IoC容器初始化的分析,以FileSystemXmlApplicationContext为例,下面是FileSystemXmlApplicationContext的继承关系~(这形状,满满的爱啊,哈哈)

image-20181203195616507

BeanDefinition的Resource定位

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">class</span> <span style="color:#a31515">FileSystemXmlApplicationContext</span> <span style="color:#0000ff">extends</span> <span style="color:#a31515">AbstractXmlApplicationContext</span> {
    <span style="color:green">/**
     * 无参构造
     */</span>
    <span style="color:#0000ff">public</span> <span style="color:#a31515">FileSystemXmlApplicationContext</span>() {
    }
    <span style="color:green">/**
     * 传入父类上下文
     */</span>
    <span style="color:#0000ff">public</span> <span style="color:#a31515">FileSystemXmlApplicationContext</span>(ApplicationContext parent) {
        <span style="color:#0000ff">super</span>(parent);
    }
    <span style="color:green">/**
     * 核心构造方法,其他几个都基于本构造方法
     * configLocations 传入xml配置文件位置集合
     * refresh 是否自动刷新容器(是refresh方法的调用,初始化上下文的核心方法)
     * parent 父类上下文
     * 1.传入配置文件地址
     * 2.刷新容器
     */</span>
    <span style="color:#0000ff">public</span> <span style="color:#a31515">FileSystemXmlApplicationContext</span>(String[] configLocations, <span style="color:#0000ff">boolean</span> refresh, ApplicationContext parent)
            <span style="color:#0000ff">throws</span> BeansException {

        <span style="color:#0000ff">super</span>(parent);
        setConfigLocations(configLocations);
        <span style="color:#0000ff">if</span> (refresh) {
            refresh();
        }
    }

    <span style="color:green">/**
     * 通过给定的路径在文件系统中定位BeanDefinition并返回一个FileSystemResource
     * 这个方法是BeanDefinitionReader的loadBeanDefinition中被调用,
     * loadBeanDefinition采用了模板模式,具体实现在不同的子类中(默认是类路径)
     */</span>
    <span style="color:#2b91af">@Override</span>
    <span style="color:#0000ff">protected</span> Resource <span style="color:#a31515">getResourceByPath</span>(String path) {
        <span style="color:#0000ff">if</span> (path != <span style="color:#0000ff">null</span> && path.startsWith(<span style="color:#a31515">"/"</span>)) {
            path = path.substring(1);
        }
        <span style="color:#0000ff">return</span> <span style="color:#0000ff">new</span> FileSystemResource(path);
    }

}
</code></span>

看以看出,本类对所有configLocation都进行了处理,使所有以xml形式存在的BeanDefinition都得到了处理,其中这个refresh就最最关键点方法,接下来对refresh进行解析。

refresh是在AbstractApplicationContext中实现,理解了refresh方法,基本就理解了IoC初始化的全过程了。

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">refresh</span>() <span style="color:#0000ff">throws</span> BeansException, IllegalStateException {
    <span style="color:#0000ff">synchronized</span> (<span style="color:#0000ff">this</span>.startupShutdownMonitor) {
        <span style="color:green">// 刷新前准备活动</span>
        prepareRefresh();

        <span style="color:green">// 关键方法构建beanFactory——>接下来会详解本方法</span>
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        <span style="color:green">// 为在这个上下文中使用beanFactory做准备</span>
        prepareBeanFactory(beanFactory);

        <span style="color:#0000ff">try</span> {
            <span style="color:green">// 设置后置处理器</span>
            postProcessBeanFactory(beanFactory);

            <span style="color:green">// 调用bean的后置处理器,这些处理器在上下文中被注册为bean的形式</span>
            <span style="color:green">// Invoke factory processors registered as beans in the context.</span>
            invokeBeanFactoryPostProcessors(beanFactory);

            <span style="color:green">// Register bean processors that intercept bean creation.</span>
            <span style="color:green">// 注册拦截bean创建的处理器</span>
            registerBeanPostProcessors(beanFactory);

            <span style="color:green">// Initialize message source for this context.</span>
            <span style="color:green">// 为上下文初始化消息源,国际化功能</span>
            initMessageSource();

            <span style="color:green">// Initialize event multicaster for this context.</span>
            <span style="color:green">// 初始化上下文的时间机制</span>
            initApplicationEventMulticaster();

            <span style="color:green">// Initialize other special beans in specific context subclasses.</span>
            <span style="color:green">// 初始化其他特殊bean在特殊上下文子类中</span>
            onRefresh();

            <span style="color:green">// Check for listener beans and register them.</span>
            <span style="color:green">// 检查监听的bean,并将他们注册到容器中</span>
            registerListeners();

            <span style="color:green">// Instantiate all remaining (non-lazy-init) singletons.</span>
            <span style="color:green">// 初始化所有的非懒加载单件</span>
            finishBeanFactoryInitialization(beanFactory);

            <span style="color:green">// Last step: publish corresponding event.</span>
            <span style="color:green">// 发布相关事件,结束refresh</span>
            finishRefresh();
        }

        <span style="color:#0000ff">catch</span> (BeansException ex) {
            <span style="color:green">// Destroy already created singletons to avoid dangling resources.</span>
            <span style="color:green">// 出现异常销毁beans</span>
            destroyBeans();

            <span style="color:green">// Reset 'active' flag.</span>
            <span style="color:green">// 这个active在上面的prepare中被设置为了true</span>
            cancelRefresh(ex);

            <span style="color:green">// Propagate exception to caller.</span>
            <span style="color:#0000ff">throw</span> ex;
        }
    }
}
</code></span>

接下来我们详细来看一下容器的构建过程,在类AbstractRefreshableApplicationContext中

<span style="color:#333333"><code><span style="color:#0000ff">protected</span> ConfigurableListableBeanFactory <span style="color:#a31515">obtainFreshBeanFactory</span>() {
    <span style="color:green">// 刷新beanfactory,这个方法很重要,就是它构建的bean,【继续往里走】</span>
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    <span style="color:#0000ff">if</span> (logger.isDebugEnabled()) {
        logger.debug(<span style="color:#a31515">"Bean factory for "</span> + getDisplayName() + <span style="color:#a31515">": "</span> + beanFactory);
    }
    <span style="color:#0000ff">return</span> beanFactory;
}</code></span>
<span style="color:#333333"><code><span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">protected</span> <span style="color:#0000ff">final</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">refreshBeanFactory</span>() <span style="color:#0000ff">throws</span> BeansException {
    <span style="color:green">// 如果已经存在beanfactory那就销毁掉bean并把工厂关了,避免对接下来的初始化造成影响</span>
    <span style="color:#0000ff">if</span> (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    <span style="color:#0000ff">try</span> {
        <span style="color:green">// 这里创建了一个DefaultListableBeanFactory</span>
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        <span style="color:green">// 设置唯一id,用于序列化</span>
        beanFactory.setSerializationId(getId());
        <span style="color:green">// 自定义bean工厂</span>
        customizeBeanFactory(beanFactory);
        <span style="color:green">// 向工厂中加载BeanDefinition,这个很重要,【继续往里走】</span>
        loadBeanDefinitions(beanFactory);
        <span style="color:#0000ff">synchronized</span> (<span style="color:#0000ff">this</span>.beanFactoryMonitor) {
            <span style="color:#0000ff">this</span>.beanFactory = beanFactory;
        }
    }
    <span style="color:#0000ff">catch</span> (IOException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> ApplicationContextException(<span style="color:#a31515">"I/O error parsing bean definition source for "</span> + getDisplayName(), ex);
    }
}</code></span>

loadBeanDefinitions在AbstractRefreshableApplicationContext中是个抽象方法,我直接找到了在子类AbstractXmlApplicationContext(其实有三个实现类,但是我们现在研究的是FileSystemXmlApplicationContext)中的实现。

<span style="color:#333333"><code><span style="color:#0000ff">protected</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">loadBeanDefinitions</span>(DefaultListableBeanFactory beanFactory) <span style="color:#0000ff">throws</span> BeansException, IOException {
    <span style="color:green">// 看这名字,显然就是用这家伙对来将xml配置文件中的信息读取放到容器里的.</span>
    XmlBeanDefinitionReader beanDefinitionReader = <span style="color:#0000ff">new</span> XmlBeanDefinitionReader(beanFactory);

    <span style="color:green">//使用这个上下文的环境资源对这个reader进行配置</span>
    beanDefinitionReader.setEnvironment(<span style="color:#0000ff">this</span>.getEnvironment());
    beanDefinitionReader.setResourceLoader(<span style="color:#0000ff">this</span>);
    beanDefinitionReader.setEntityResolver(<span style="color:#0000ff">new</span> ResourceEntityResolver(<span style="color:#0000ff">this</span>));

    <span style="color:green">// 允许子类个性化对这个reader初始化</span>
    initBeanDefinitionReader(beanDefinitionReader);
    <span style="color:green">// 然后开始真正的加载BeanDefinition,【继续往里走】</span>
    loadBeanDefinitions(beanDefinitionReader);
}</code></span>

这次是跳转到AbstractXmlApplicationContext里面,继续阅读

<span style="color:#333333"><code><span style="color:#0000ff">protected</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">loadBeanDefinitions</span>(XmlBeanDefinitionReader reader) <span style="color:#0000ff">throws</span> BeansException, IOException {
    <span style="color:green">// 获取配置资源~~不过直接返回了一个null,不过有子类进行重写的(如果看过HashMap源码,那肯定记得里面有几个空实现是给LinkedHashMap用的,这里同样的道理)</span>
    Resource[] configResources = getConfigResources();
    <span style="color:#0000ff">if</span> (configResources != <span style="color:#0000ff">null</span>) {
        reader.loadBeanDefinitions(configResources);
    }
    <span style="color:green">// 这个是类AbstractRefreshableConfigApplicationContext中的方法(跳来跳去脑壳都大了。。。)</span>
    <span style="color:green">// FileSystemXmlApplicationContext在refresh前就设置了</span>
    String[] configLocations = getConfigLocations();
    <span style="color:#0000ff">if</span> (configLocations != <span style="color:#0000ff">null</span>) {
        <span style="color:green">// 终于开始解析了,【继续往里走】</span>
        reader.loadBeanDefinitions(configLocations);
    }
}</code></span>

AbstractBeanDefinitionReade中的方法,就是在这里进行加载的

<span style="color:#333333"><code><span style="color:#2b91af">@Override</span>
<span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">loadBeanDefinitions</span>(String... locations) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    Assert.notNull(locations, <span style="color:#a31515">"Location array must not be null"</span>);
    <span style="color:#0000ff">int</span> counter = 0;
    <span style="color:#0000ff">for</span> (String location : locations) {
        <span style="color:green">// ....【继续往里面走】</span>
        counter += loadBeanDefinitions(location);
    }
    <span style="color:#0000ff">return</span> counter;
}</code></span>

还在本类里~不用跳了

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">loadBeanDefinitions</span>(String location, Set<Resource> actualResources) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    <span style="color:green">// 获取资源加载器</span>
    ResourceLoader resourceLoader = getResourceLoader();
    <span style="color:green">// 空就抛异常</span>
    <span style="color:#0000ff">if</span> (resourceLoader == <span style="color:#0000ff">null</span>) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(
            <span style="color:#a31515">"Cannot import bean definitions from location ["</span> + location + <span style="color:#a31515">"]: no ResourceLoader available"</span>);
    }
    <span style="color:green">// 这个是用来解析classpath*:这种的路径,可以是多个配置文件</span>
    <span style="color:#0000ff">if</span> (resourceLoader <span style="color:#0000ff">instanceof</span> ResourcePatternResolver) {
        <span style="color:green">// Resource pattern matching available.</span>
        <span style="color:#0000ff">try</span> {
            <span style="color:green">//到这里getResource【完成了具体的定位】</span>
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            <span style="color:green">// 开始载入BeanDefinition</span>
            <span style="color:#0000ff">int</span> loadCount = loadBeanDefinitions(resources);
            <span style="color:#0000ff">if</span> (actualResources != <span style="color:#0000ff">null</span>) {
                <span style="color:#0000ff">for</span> (Resource resource : resources) {
                    actualResources.add(resource);
                }
            }
            <span style="color:#0000ff">if</span> (logger.isDebugEnabled()) {
                logger.debug(<span style="color:#a31515">"Loaded "</span> + loadCount + <span style="color:#a31515">" bean definitions from location pattern ["</span> + location + <span style="color:#a31515">"]"</span>);
            }
            <span style="color:#0000ff">return</span> loadCount;
        }
        <span style="color:#0000ff">catch</span> (IOException ex) {
            <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(
                <span style="color:#a31515">"Could not resolve bean definition resource pattern ["</span> + location + <span style="color:#a31515">"]"</span>, ex);
        }
    }
    <span style="color:#0000ff">else</span> {
        <span style="color:green">// 到这里getResource,resource接口中封装了很多与I/O相关的操作</span>
        <span style="color:green">// 至此【完成了具体的定位】</span>
        Resource resource = resourceLoader.getResource(location);
        <span style="color:green">// 开始载入BeanDefinition【继续往里跳】</span>
        <span style="color:#0000ff">int</span> loadCount = loadBeanDefinitions(resource);
        <span style="color:#0000ff">if</span> (actualResources != <span style="color:#0000ff">null</span>) {
            actualResources.add(resource);
        }
        <span style="color:#0000ff">if</span> (logger.isDebugEnabled()) {
            logger.debug(<span style="color:#a31515">"Loaded "</span> + loadCount + <span style="color:#a31515">" bean definitions from location ["</span> + location + <span style="color:#a31515">"]"</span>);
        }
        <span style="color:#0000ff">return</span> loadCount;
    }
}</code></span>

refresh方法完成IoC的整个初始化,其中 refreshBeanFactory()方法非常的重要,本小节讲到定位,下一小节开始讲解BeanDefinition解析与加载。

BeanDefinition的载入和解析

对于IoC容器来说,这个载入过程相当于把xml中的BeanDefinition转换成一个Spring内部的数据结构的过程。IoC容器对Bean的管理和依赖注入功能是通过对其持有的BeanDefinition进行各种相关操作来实现的。这些BeanDefinition是通过一个HashMap来实现的。

image-20181204074701504

承接上文,loadBeanDefinitions()是对BeanDefinition载入和解析的核心方法。具体实现在XMLBeanDefinitionReader里面。

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">loadBeanDefinitions</span>(Resource... resources) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    Assert.notNull(resources, <span style="color:#a31515">"Resource array must not be null"</span>);
    <span style="color:#0000ff">int</span> counter = 0;
    <span style="color:#0000ff">for</span> (Resource resource : resources) {
        <span style="color:green">// 这里是循环加载,【继续往里面跳】</span>
        counter += loadBeanDefinitions(resource);
    }
    <span style="color:#0000ff">return</span> counter;
}</code></span>
<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">loadBeanDefinitions</span>(Resource resource) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    <span style="color:green">// 对Resource进行包装,提供通过不同编码方式读取资源文件,【继续往里面跳】</span>
    <span style="color:#0000ff">return</span> loadBeanDefinitions(<span style="color:#0000ff">new</span> EncodedResource(resource));
}</code></span>
<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">loadBeanDefinitions</span>(EncodedResource encodedResource) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    Assert.notNull(encodedResource, <span style="color:#a31515">"EncodedResource must not be null"</span>);
    <span style="color:#0000ff">if</span> (logger.isInfoEnabled()) {
        logger.info(<span style="color:#a31515">"Loading XML bean definitions from "</span> + encodedResource.getResource());
    }
    <span style="color:green">// 获取已经加载的Resource</span>
    Set<EncodedResource> currentResources = <span style="color:#0000ff">this</span>.resourcesCurrentlyBeingLoaded.get();
    <span style="color:#0000ff">if</span> (currentResources == <span style="color:#0000ff">null</span>) {
        currentResources = <span style="color:#0000ff">new</span> HashSet<EncodedResource>(4);
        <span style="color:#0000ff">this</span>.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    <span style="color:green">// 解决重复依赖问题,encodedResource的equals方法已经被重写</span>
    <span style="color:#0000ff">if</span> (!currentResources.add(encodedResource)) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(
            <span style="color:#a31515">"Detected cyclic loading of "</span> + encodedResource + <span style="color:#a31515">" - check your import definitions!"</span>);
    }
    <span style="color:green">// 这里获取IO准备读取XML中的BeanDefinition</span>
    <span style="color:#0000ff">try</span> {
        InputStream inputStream = encodedResource.getResource().getInputStream();
        <span style="color:#0000ff">try</span> {
            InputSource inputSource = <span style="color:#0000ff">new</span> InputSource(inputStream);
            <span style="color:#0000ff">if</span> (encodedResource.getEncoding() != <span style="color:#0000ff">null</span>) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            <span style="color:green">// 【继续往里面跳】</span>
            <span style="color:#0000ff">return</span> doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        <span style="color:#0000ff">finally</span> {
            inputStream.close();
        }
    }
    <span style="color:#0000ff">catch</span> (IOException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(
            <span style="color:#a31515">"IOException parsing XML document from "</span> + encodedResource.getResource(), ex);
    }
    <span style="color:#0000ff">finally</span> {
        currentResources.remove(encodedResource);
        <span style="color:#0000ff">if</span> (currentResources.isEmpty()) {
            <span style="color:#0000ff">this</span>.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}
</code></span>

这个是在XMLBeanDefinitionReader中实现(没有进其他类)

<span style="color:#333333"><code><span style="color:#0000ff">protected</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">doLoadBeanDefinitions</span>(InputSource inputSource, Resource resource)
    <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    <span style="color:#0000ff">try</span> {
        <span style="color:green">// 这里取得XML的document对象,解析由documentLoader完成,感兴趣看以进去看看步骤</span>
        <span style="color:green">// 虽然已经对xml进行解析但是并没有按照bean的规则,所以需要继续解析</span>
        Document doc = doLoadDocument(inputSource, resource);
        <span style="color:green">// 这里启动的是对beanDefinition解析的详细过程,很重要的方法,【继续往里面跳】</span>
        <span style="color:#0000ff">return</span> registerBeanDefinitions(doc, resource);
    }
    <span style="color:#0000ff">catch</span> (BeanDefinitionStoreException ex) {
        <span style="color:#0000ff">throw</span> ex;
    }
    <span style="color:#0000ff">catch</span> (SAXParseException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> XmlBeanDefinitionStoreException(resource.getDescription(),
                                                  <span style="color:#a31515">"Line "</span> + ex.getLineNumber() + <span style="color:#a31515">" in XML document from "</span> + resource + <span style="color:#a31515">" is invalid"</span>, ex);
    }
    <span style="color:#0000ff">catch</span> (SAXException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> XmlBeanDefinitionStoreException(resource.getDescription(),
                                                  <span style="color:#a31515">"XML document from "</span> + resource + <span style="color:#a31515">" is invalid"</span>, ex);
    }
    <span style="color:#0000ff">catch</span> (ParserConfigurationException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(resource.getDescription(),
                                               <span style="color:#a31515">"Parser configuration exception parsing XML from "</span> + resource, ex);
    }
    <span style="color:#0000ff">catch</span> (IOException ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(resource.getDescription(),
                                               <span style="color:#a31515">"IOException parsing XML document from "</span> + resource, ex);
    }
    <span style="color:#0000ff">catch</span> (Throwable ex) {
        <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(resource.getDescription(),
                                               <span style="color:#a31515">"Unexpected exception parsing XML document from "</span> + resource, ex);
    }
}</code></span>

仍然在本类中

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">int</span> <span style="color:#a31515">registerBeanDefinitions</span>(Document doc, Resource resource) <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {
    <span style="color:green">// 创建BeanDefinitionDocumentReader对document进行解析</span>
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    documentReader.setEnvironment(<span style="color:#0000ff">this</span>.getEnvironment());
    <span style="color:#0000ff">int</span> countBefore = getRegistry().getBeanDefinitionCount();
    <span style="color:green">// 具体的解析过程,【继续往里面跳】</span>
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    <span style="color:#0000ff">return</span> getRegistry().getBeanDefinitionCount() - countBefore;
}</code></span>

进入DefaultBeanDefinitionDocumentReader类中,在文档元素中获取根元素,并继续调用doRegisterBeanDefinition进行注册。

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">registerBeanDefinitions</span>(Document doc, XmlReaderContext readerContext) {
    <span style="color:#0000ff">this</span>.readerContext = readerContext;
    logger.debug(<span style="color:#a31515">"Loading bean definitions"</span>);
    Element root = doc.getDocumentElement();
    <span style="color:green">// 【继续往里面跳】</span>
    doRegisterBeanDefinitions(root);
}</code></span>
<span style="color:#333333"><code><span style="color:#0000ff">protected</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">doRegisterBeanDefinitions</span>(Element root) {
    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    <span style="color:#0000ff">if</span> (StringUtils.hasText(profileSpec)) {
        Assert.state(<span style="color:#0000ff">this</span>.environment != <span style="color:#0000ff">null</span>, <span style="color:#a31515">"Environment must be set for evaluating profiles"</span>);
        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        <span style="color:#0000ff">if</span> (!<span style="color:#0000ff">this</span>.environment.acceptsProfiles(specifiedProfiles)) {
            <span style="color:#0000ff">return</span>;
        }
    }
    BeanDefinitionParserDelegate parent = <span style="color:#0000ff">this</span>.delegate;
    <span style="color:#0000ff">this</span>.delegate = createDelegate(<span style="color:#0000ff">this</span>.readerContext, root, parent);

    preProcessXml(root);
    <span style="color:green">//  对BeanDefinition进行解析,该方法的核心逻辑,【继续往里面跳】</span>
    parseBeanDefinitions(root, <span style="color:#0000ff">this</span>.delegate);
    postProcessXml(root);

    <span style="color:#0000ff">this</span>.delegate = parent;
}</code></span>
<span style="color:#333333"><code><span style="color:green">// 不难看出,这是对xml的解析过程</span>
<span style="color:#0000ff">protected</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">parseBeanDefinitions</span>(Element root, BeanDefinitionParserDelegate delegate) {
    <span style="color:#0000ff">if</span> (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        <span style="color:#0000ff">for</span> (<span style="color:#0000ff">int</span> i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            <span style="color:#0000ff">if</span> (node <span style="color:#0000ff">instanceof</span> Element) {
                Element ele = (Element) node;
                <span style="color:#0000ff">if</span> (delegate.isDefaultNamespace(ele)) {
                    <span style="color:green">// 本逻辑为核心逻辑,【继续往里面跳】</span>
                    parseDefaultElement(ele, delegate);
                }
                <span style="color:#0000ff">else</span> {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    <span style="color:#0000ff">else</span> {
        delegate.parseCustomElement(root);
    }
}</code></span>
<span style="color:#333333"><code><span style="color:#0000ff">private</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">parseDefaultElement</span>(Element ele, BeanDefinitionParserDelegate delegate) {
    <span style="color:green">// import标签</span>
    <span style="color:#0000ff">if</span> (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    <span style="color:green">// 别名</span>
    <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    <span style="color:green">// bean标签</span>
    <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        <span style="color:green">// 对bean标签进行解析,【继续往里面跳】</span>
        processBeanDefinition(ele, delegate);
    }
    <span style="color:green">// beans标签</span>
    <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        <span style="color:green">// recurse</span>
        doRegisterBeanDefinitions(ele);
    }
}</code></span>
<span style="color:#333333"><code><span style="color:#0000ff">protected</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">processBeanDefinition</span>(Element ele, BeanDefinitionParserDelegate delegate) {
    <span style="color:green">// 这个是BeanDefinitionHolder里装有BeanDefinition对象和beanname,别名集合等信息</span>
    <span style="color:green">// parseBeanDefinitionElement()这个方法将xml中bean的定义进行解析,有兴趣可以进去深入了解</span>
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    <span style="color:#0000ff">if</span> (bdHolder != <span style="color:#0000ff">null</span>) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        <span style="color:#0000ff">try</span> {
            <span style="color:green">// 向IoC容器注册解析到的BeanDefinition,【继续往里面跳】</span>
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        <span style="color:#0000ff">catch</span> (BeanDefinitionStoreException ex) {
            getReaderContext().error(<span style="color:#a31515">"Failed to register bean definition with name '"</span> +
                                     bdHolder.getBeanName() + <span style="color:#a31515">"'"</span>, ele, ex);
        }
        <span style="color:green">// Send registration event.</span>
        getReaderContext().fireComponentRegistered(<span style="color:#0000ff">new</span> BeanComponentDefinition(bdHolder));
    }
}</code></span>

至此,XML中的BeanDefinition的解析和载入全部完成,接下来进入bean的注册部分。

BeanDefinition在IoC容器中的注册

经过定位和载入后,BeanDefinition已经在IoC建立起相应的数据结构,为了更友好的使用这些BeanDefinition,需要在IoC容器中将这些BeanDefinition进行注册。

该方法在BeanDefinitionReaderUtils类中

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">static</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">registerBeanDefinition</span>(
    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {

    String beanName = definitionHolder.getBeanName();
    <span style="color:green">// 这个很明显是将BeanDefinition注册的方法,【继续往里面跳】</span>
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    <span style="color:green">// Register aliases for bean name, if any.</span>
    String[] aliases = definitionHolder.getAliases();
    <span style="color:#0000ff">if</span> (aliases != <span style="color:#0000ff">null</span>) {
        <span style="color:#0000ff">for</span> (String aliase : aliases) {
            registry.registerAlias(beanName, aliase);
        }
    }
}</code></span>

跳转到DefaultListableBeanFactory类中,前面创建工厂时用的就是这个工厂

<span style="color:#333333"><code><span style="color:#0000ff">public</span> <span style="color:#0000ff">void</span> <span style="color:#a31515">registerBeanDefinition</span>(String beanName, BeanDefinition beanDefinition)
    <span style="color:#0000ff">throws</span> BeanDefinitionStoreException {

    Assert.hasText(beanName, <span style="color:#a31515">"Bean name must not be empty"</span>);
    Assert.notNull(beanDefinition, <span style="color:#a31515">"BeanDefinition must not be null"</span>);

    <span style="color:#0000ff">if</span> (beanDefinition <span style="color:#0000ff">instanceof</span> AbstractBeanDefinition) {
        <span style="color:#0000ff">try</span> {
            ((AbstractBeanDefinition) beanDefinition).validate();
        }
        <span style="color:#0000ff">catch</span> (BeanDefinitionValidationException ex) {
            <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                   <span style="color:#a31515">"Validation of bean definition failed"</span>, ex);
        }
    }
    <span style="color:green">// 为了保证数据一致性,注册时加个synchronized线程锁</span>
    <span style="color:#0000ff">synchronized</span> (<span style="color:#0000ff">this</span>.beanDefinitionMap) {
        <span style="color:green">// 检查在IoC容器中是否有同名bean,有同名的还不让覆盖的就是抛异常</span>
        BeanDefinition oldBeanDefinition = <span style="color:#0000ff">this</span>.beanDefinitionMap.get(beanName);
        <span style="color:#0000ff">if</span> (oldBeanDefinition != <span style="color:#0000ff">null</span>) {
            <span style="color:#0000ff">if</span> (!<span style="color:#0000ff">this</span>.allowBeanDefinitionOverriding) {
                <span style="color:#0000ff">throw</span> <span style="color:#0000ff">new</span> BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                       <span style="color:#a31515">"Cannot register bean definition ["</span> + beanDefinition + <span style="color:#a31515">"] for bean '"</span> + beanName +
                                                       <span style="color:#a31515">"': There is already ["</span> + oldBeanDefinition + <span style="color:#a31515">"] bound."</span>);
            }
            <span style="color:#0000ff">else</span> <span style="color:#0000ff">if</span> (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                <span style="color:#0000ff">if</span> (<span style="color:#0000ff">this</span>.logger.isWarnEnabled()) {
                    <span style="color:#0000ff">this</span>.logger.warn(<span style="color:#a31515">"Overriding user-defined bean definition for bean '"</span> + beanName +<span style="color:#a31515">" with a framework-generated bean definition ': replacing ["</span> +oldBeanDefinition + <span style="color:#a31515">"] with ["</span> + beanDefinition + <span style="color:#a31515">"]"</span>);
                }
            }
            <span style="color:#0000ff">else</span> {
                <span style="color:#0000ff">if</span> (<span style="color:#0000ff">this</span>.logger.isInfoEnabled()) {
                    <span style="color:#0000ff">this</span>.logger.info(<span style="color:#a31515">"Overriding bean definition for bean '"</span> + beanName +<span style="color:#a31515">"': replacing ["</span> + oldBeanDefinition + <span style="color:#a31515">"] with ["</span> + beanDefinition + <span style="color:#a31515">"]"</span>);
                }
            }
        }
        <span style="color:#0000ff">else</span> {
            <span style="color:#0000ff">this</span>.beanDefinitionNames.add(beanName);
            <span style="color:#0000ff">this</span>.frozenBeanDefinitionNames = <span style="color:#0000ff">null</span>;
        }
        <span style="color:green">// 把BeanDefinition装到如到beanDefinitionMap中</span>
        <span style="color:green">// 【至此Spring IoC容器初始化完成~】</span>
        <span style="color:green">// beanDeinitionMap是初始长度64的ConcurrentHashMap</span>
        <span style="color:#0000ff">this</span>.beanDefinitionMap.put(beanName, beanDefinition);
    }

    resetBeanDefinition(beanName);
}</code></span>

到这里,注册完成。我们创建bean工厂,将BeanDefinition注册到了IoC容器持有的Map中。这些信息是控制反转的基础。

三、小结

本文开始简略解释IoC的概念,然后从FileSystemXmlApplicationContext着手,根据源码一步一步讲述从bean工厂创建到BeanDefinition在IoC中注册核心逻辑。Spring源码确实细节太多,在阅读源码过程中,一定要抓住核心逻辑。

本文是博主在学习Spring源码过程中对IoC的总结,希望对想要阅读源码但不知从何下手的同学有所帮助!如有错误希望大家指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值