Spring源码解读,Spring容器核心类

知识要点:

BeanFactory

ApplicationContext

BeanDefinition

XMLBeanDefinitionReader

我们会通过一小段代码来帮助了解Spring的容器核心类,以及Spring的启动流程和一些主要的细节工作。
在我们使用Spring的时候总是会有一个入口,这里我们使用XML方式而非注解方式,因为这样的方式便于大家理解Spring的核心类和工作流程。下面就来看看我们基于XML配置文件方式时是如何使用Spring的。
使用 Spring 时,XML 和注解是两种使用得最多的配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要体现在 BeanDefinition 的解析上。其对于核心的容器启动流程是一致的。
首先是Spring基于XML格式的配置文件beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.app.TestBean"/>

</beans>

下来,我们通过一段简单的代码来看看如何加载XML配置文件,并可以在代码中直接使用TestBean(期间我们并没有显式地new过这个JavaBean)

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpring {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx =
                new ClassPathXmlApplicationContext("beans.xml");
        System.out.println(ctx.getId());
        TestBean tb = ctx.getBean(TestBean.class);
        tb.test();
    }
}

需要交给Spring来进行管理的JavaBean:

public class TestBean {
    public TestBean() {
        System.out.println("init.......");
    }

    public void test() {
        System.out.println("test.......");
    }
}

我们跟踪代码的执行过程发现:AbstractApplicationContext中的refresh方式完成了很多事情。而AbstractApplicationContext有非常多的成员变量,如:ResourcePatternResolver、LifecycleProcessor、MessageSource、ApplicationListener、ApplicationEvent等类,这也体现了为什么我们经常在Spring文章或者教程中一旦提到ApplicationContext,就会告诉我们它提供了上下文分层支持、消息国际化、资源访问、事件传播等功能。

BeanFactory

BeanFactory为Spring的IoC容器提供了基础功能。它主要被用于与Spring其他部分以及相关的第三方框架集成,并且它的子类实现DefaultListableBeanFactory是更高级别的GenericApplicationContext容器中的关键委托。
Spring Bean的创建是典型的工厂模式,这一系列的Bean工厂(即IoC容器)为开发者管理对象的依赖关系提供了很多便利,在Spring中有许多的IoC容器实现供用户选择和使用,其相互关系如下:


工厂模式

工厂模式(Factory Pattern)是最常用的设计模式之一。这种设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,而是通过使用一个共同的接口来指向新创建的对象。

简单工厂

简单工厂模式不是23种里的模式。它的代码非常简单:根据传入的参数,生成对应的产品对象:

interface Phone {
    void run();
}

class Redmi implements Phone {
    @Override
    public void run() {
        System.out.println("Redmi");
    }
}

class Apple implements Phone {
    @Override
    public void run() {
        System.out.println("apple");
    }
}

public class PhoneFactory {
    public Phone createPhone(String type) {
        if (type.equals("redmi")) {
            return new Redmi();
        } else if (type.equals("apple")) {
            return new Apple();
        }
        return null;
    }
}

这种方式,想添加一种新的产品时,就必须修改工厂类。显然,简单工厂违反了开闭原则。所以简单工厂只适合产品对象较少,且需求固定的场景,而对于产品丰富,需求不固定的场景来说不合适。

工厂方法

定义一个创建对象的接口,让子类决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

interface PhoneFactory {
    // 手机生产线
    Phone createPhone();
}

class AppleFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new Apple();
    }
}

class XiaomiFactory implements PhoneFactory {
    @Override
    public Redmi createPhone() {
        return new Redmi();
    }
}

这种方式,如果需要很多产品的话,需要创建非常多的工厂,所以这种方式的缺点也很明显。

抽象工厂

为创建一组相关或者是相互依赖的对象提供的一个接口,而不需要指定它们的具体类。抽象工厂和工厂方法的模式基本一样,区别在于:工厂方法是生产一个具体的产品,而抽象工厂可以用来生产一组产品。

interface Pad {
    // 生产Pad
    void run();
}

class XiaomiPad implements Pad {
    @Override
    public void run() {
        System.out.println("xiaomi pad");
    }
}

class IPad implements Pad {
    @Override
    public void run() {
        System.out.println("apple ipad");
    }
}

interface PhoneFactory {
    // 手机生产线
    Phone createPhone();
}

interface PadFactory {
    // 平板生产线
    Pad createPad();
}

interface Factory extends PhoneFactory, PadFactory {
}

class AppleFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new Apple();
    }
    
     @Override
    public Pad createPad() {
        return new IPad();
    }
}

class XiaomiFactory implements PhoneFactory {
    @Override
    public Redmi createPhone() {
        return new Redmi();
    }

    @Override
    public Pad createPhone() {
        return new XiaomiPad();
    }
}

DefaultListableBeanFactory

DefaultListableBeanFactory是Bean加载的最核心部分,也是Spring注册及管理加载Bean的默认实现。DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接口。下面是DefaultListableBeanFactory继承关系图:



类图中的类作用描述如下:

AliasRegistry 定义对alias的简单增删改查等操作
BeanDefinitionRegistry 向注册表中注册 BeanDefinition 实例,完成注册程
SimpleAliasRegistry 主要使用map作为alias的缓存,并对接口AliasRegistry进行实现
SingletonBeanRegistry 定义对单例的注册及获取
BeanFactory 定义获取Bean及各种属性
DefaultSingletonBeanRegistry 对接口SingletonBeanRegistry各函数的实现
ListableBeanFactory 根据各种条件获取Bean的列表
HierarchicalBeanFactory 继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parent BeanFactory的支持
FactoryBeanRegistrySupport 在DefaultSingletonBeanRegistry的基础上增加了对FactoryBean的处理功能
ConfigurableBeanFactory 提供配置Factory的各种方法
AutowireCapableBeanFactory 提供创建Bean、自动注入,初始化以及应用Bean的后处理器
AbstractBeanFactory FactoryBeanRegistrySupport和ConfigurableBeanFactory功能的集合
AbstractAutowireCapableBeanFactory 综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
ConfigurableListableBeanFactory BeanFactory配置清单,指定忽略类型及接口等
DefaultListableBeanFactory 综合上面所有的功能

ApplicationContext

ApplicationContext接口是由BeanFactory接口派生出来的,所以提供了BeanFactory的所有功能。除此之外,ApplicationContext还提供了如下功能:

  • 通过MessageSource访问i18n消息。
  • 通过ResourceLoader访问资源,如:URL和文件。
  • 使用ApplicationEventPublisher接口,将事件发布到实现ApplicationListener接口的Bean。
  • 加载多个(分层)上下文,从而允许每个上下文通过HierarchicalBeanFactory接口集中在一个特定层上。如:Web层。


BeanFactory和ApplicationContext

BeanFactory的核心概念就是Bean工厂,用于Bean生命周期的管理,而Applicationcontext除了具有BeanFactory的特性外,还包括消息国际化、资源访问、事件传播等功能。简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext则添加了更多特定的功能。ApplicationContext是BeanFactory的完整超集。
除非您有充分的理由,否则请使用ApplicationContext。因为ApplicationContext包含BeanFactory的所有功能,除非你需要完全控制Bean处理的方案。

特点 BeanFactory ApplicationContext
Bean实例化/装配 Yes Yes
集成的生命周期管理 No Yes
自动注册 BeanPostProcessor No Yes
自动注册BeanFactoryPostProcessor No Yes
便利的 MessageSource访问 No Yes
内置的ApplicationEvent发布机制 No Yes

BeanDefinition

Spring IoC容器管理一个或多个Bean。这些Bean是根据程序提供给容器的配置元数据创建的,如:以XML <bean />格式的定义。而在容器内部,这些Bean需要表示为BeanDefinition对象,也就是有一个将Bean解析成Spring内部的BeanDefinition对象的过程。BeanDefinition包含以下元数据信息:

  • 一个全限定的类名
  • 用于声明Bean在容器中的行为信息(作用域,生命周期回调等)。
  • 要完成自身工作需要引用其他的Bean,这些引用也称为依赖项。
  • 要在新创建的对象中设置的其他配置,如:用于管理连接池的连接数,或池的大小限制。
    这些元数据构成每个BeanDefinition的一组属性:
  • class
  • name:Bean在容器内的唯一标识符。基于XML的配置,可以使用id或name属来指定Bean标识符
  • scope:Bean的作用域
  • constructor arguments:构造函数的参数
  • properties:Bean包含的属性(依赖注入项)
  • autowiring mode:自动装配模式
  • lazy-initialization mode:延迟加载方法
  • initialization method:初始化方法
  • destruction method:销毁方法
    BeanDefinition继承了AttributeAccessor和BeanMetadataElement接口:
  • AttributeAccessor:提供了访问属性的能力
  • BeanMetadataElement:用来获取元数据元素的配置源对象

BeanDefinition常见实现类

  • ChildBeanDefinition
  • RootBeanDefinition
  • GenericBeanDefinition
  • AnnotatedGenericBeanDefinition
  • ScannedGenericBeanDefinition

XMLBeanDefinitionReader

XML配置文件的读取时Spring的重要功能,因为Spring的大部分功能都是以配置作为切入点的。

BeanDefinitionReader 主要定义资源文件读取并转换为BeanDefinition的各个功能
EnvironmentCapable 定义获取Environment方法
DocumentLoader 定义从资源文件加载到转换为Document的功能
AbstractBeanDefinitionReader 对EnvironmentCapable、BeanDefinitionReader类定义的功能进行实现
BeanDefinitionDocumentReader 定义读取Document并注册BeanDefiniton功能
BeanDefinitionParserDelegate 定义解析Element的各种方法

在XmlBeanDifinitonReader中主要包含以下几个步骤的处理:

  • 通过继承自AbstractBeanDefinitionReader中的方法,来使用ResourceLoader将资源文件路径转换为对应的Resource文件。
  • 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件。
  • 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,154评论 5 464
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,252评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,107评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,985评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,905评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,256评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,978评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,611评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,891评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,910评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,736评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,516评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,995评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,132评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,447评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,034评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,242评论 2 339

推荐阅读更多精彩内容