Spring源码--BeanFactory

BeanFactory

Spring Ioc 是一个管理Bean的容器,在Spring的定义中,他要求所有的Ioc容器都需要实现接口BeanFactory。==BeanFactory是一个顶级容器接口。==

public interface BeanFactory {

    // 前缀
    String FACTORY_BEAN_PREFIX = "&";

    // 根据名称获取bean
    Object getBean(String name) throws BeansException;

    // 根据名称获取bean,返回指定类型
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;

    // 根据名称获取bean,使用指定的参数初始化
    Object getBean(String name, Object... args) throws BeansException;

    // 根据类型获取bean
    <T> T getBean(Class<T> requiredType) throws BeansException;

    // 根据类型获取bean,使用指定的参数初始化
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    // 根据类型获取bean提供者
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    // 是否存在指定名称的bean
    boolean containsBean(String name);

    // 指定的bean是否是单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    // 指定的bean是否是原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    // 是否类型匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    // 获取指定bean的类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;

    // 获取bean的别名
    String[] getAliases(String name);

}

BeanFactory中含有多个getBean方法,这是Ioc容器最重要的方法之一,主要是从Ioc容器中获取Bean。这些getBean的方法中有按类型、按名称获取bean,这也是说,在Spring Ioc容器中,允许按照名称或者类型获取bean.

由于BeanFacttory的功能还不够强大,因此Spring在BeanFactory的基础上,还设计了更为高级的接口:ApplicationContext,这是BeanFactory的子接口之一,在Spring的体系中BeanFactoryApplicationContext是最为重要的接口设计,实际使用的大部分Spring Ioc容器是ApplicationContext接口的实现类。

ApplicationContext

ApplicationContext接口通过集成上级接口,进而集成BeanFactory接口。在BeanFactory的基础上,扩展了消息国际化接口(MessageSource)和资源模式解析接口(ResourcePatternResolver)。

容器的基本使用

我们在idea中创建一个springboot的工程,只需要lombok和web的starter即可。(gradle)

image-20200820193932622

我们这里创建了spring boot的工程,但是只是使用gradle自动下载spring的依赖即可。

还记得一个spring项目是如何创建的吗?

复习一下:

spring--hello~!(如何搭建一个spring项目)

spring核心容器创建的两种方式

省去依赖包,我们创建一个spring的项目

image-20200820194405030

新增bean

image-20200820194429874

新增beans.xml

image-20200820194453702

然后注释掉spring boot中原来的代码,添加我们的代码

image-20200820194528160

运行

dGzMut.gif

可以看到,最终,我们打印出了people的name.也就是说,通过XmlBeanFactory加载了我们制定的配置文件后,使用指定的配置文件,创建了我们需要的Ioc容器。最后我们可以通过名字获取bean。

XmlBeanFactory源码

虽然XmlBeanFactory基本上没有人在使用,而且也被标记为废弃了,但是,作为我spring入门的一个方法,我还是想看看其具体的实现。

这是XmlBeanFactory的UML图:

image

我们可以看到,XmlBeanFactory直接继承DefaultListableBeanFactory,而DefaultListableBeanFactory上面又有非常多的继承关系。

  • AliasRegistry:定义对alias的简单增删改等操作。
  • SimpleAliasRegistry:主要使用Map作为alias的缓存,并对接口AliasRegistry进行实现。
  • SingletonBeanRegistry:定义对单例的注册及获取。
  • BeanFactry:定义获取bean及bean的各种特性。
  • DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry的实现。
  • HierarchicalBeanFactory:继承BeanFactory,也就是在BeanFactory定义的功能的基础上增加了对parentFactory的支持。
  • BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作。
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对BeanFactory的特殊处理。
  • ConfigurableBeanFactory:提供配置Factory的各种方法。
  • ListableBeanFactory:根据各种条件获取bean的配置清单。
  • AbstractBeanFactory:综合FactoryBeanRegistrySupportConfigurableBeanFactory的功能。
  • AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器。
  • AbstractAutowireCapableBeanFactoryA:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现。
  • ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等。
  • DefaultListableBeanFactory:综合上述全部功能,主要是对Bean进行注册后的处理。

XmlBeanFactoryDefaultListableBeanFactory进行了扩展,从Xml文档中读取BeanDefinition,对于注册和获取都是使用的父类继承的方法。

image-20200820202243293

XmlBeanDefinitionReader

这是XmlBeanDefinitionReader的UML图

image

这些类主要的操作:

  • ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource。
  • BeanDefinitionReader:主要定义自语言文件读取并转换为BeanDefinition的各个功能。
  • EnvironmentCapable:定义获取Environment方法。
  • DockumentLoader:定义从资源文件加载到转换为Document的功能。
  • AbstractBeanDefinitionReader:对EnvironmentCapable,BeanDefinitionReader类定义的功能进行实现。
  • BeanDefinitionDocumentReader定义读取Document并注册BeanDefinition功能。
  • BeanDefinitionParserDelegate:定义解析Element的各种方法。

XmlBeanFactory

我们看下XmlBeanFactory的时序图:

image

在main方法中首先调用ClassPathResource的构造方法来构造Resource资源文件的实例对象,这样后续的资源处理就可以用Resource提供的服务进行操作,有了Resource后,就可以进行XmlBeanFactory的初始化了。

配置文件的封装

Spring的配置文件读取是通过ClassPathResource进行封装的,比如new ClassPathResource("beans.xml")

在java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源的资源的读取逻辑,一般handler的类型使用不同的前缀来识别,比如:file:,http:,jar:等。但是URL没有默认定义相对ClassPath或者ServletContext等资源的handler,虽然可以注册自己的URLStreamHandler来解析特定的URL前缀(比如cclasspath:),这样需要了解URL的实现机制,而且URL也没有提供基本方法检查资源是否存在等。所以Spring对内部使用到的资源资源实现了自己的抽象结构:Resource

这是Resource的方法

image-20200822144138299

Resource接口抽象了所有Spring内部使用到的底层资源:File,URL,Classpath等。

定义了3个判断当前资源状态的方法:存在性(exists)、可读性(isReadable)、是否处于打开状态(isOpen)。另外,Resource接口还提供了不同资源到URL、URI、File类型的转换,以及获取lastModified属性、文件名(不带路径信息的文件名,getFilename())的方法。为了便于操作,Resource 还提供了基于当前资源创建一个相对资源的方法:createRelativeO。在错误处理中需要详细地打印出错的资源文件,因而 Resource还提供了 getDescription()方法用于在错误处理中的打印信息。

image-20200822143834013

不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource),ClassPath资源(ClassPathResource),URL资源(UrlResource),InputStream资源(InputStreamResource),Byte数组(ByteArrayResource)等。

具体实现也很简单,ClassPathResource是通过ClassLoader进行读取文件的:

image-20200822145454635

得到了配置流后,就交给了XmlBeanDefinitionReader进行解析。

image-20200822145716503
image-20200822145729279

在初始化父类的时候,忽略装配一些类

image-20200822145953961

加载Bean

image
  1. 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodeResource类进行封装。
  2. 获取输入流。从Resource中获取对应的InputSttream并构造InputSource
  3. 通过构造的InputSource实例和Resource实例继续调用方法doLoadBeanDefinitions
image-20200822152733034

EncodeedResource主要用于对资源文件的编码进行相应的编码处理。

image-20200822154027396

如果设置了编码属性,会使用相应的编码作为输入流的编码。

image.png

这个方法主要是将重新编码的输入流转换为SAX的InputSource对象,同时如果指定了编码,需要设置相关的属性。


image.png

这个方法处理了两件事情:

  • 加载XML文件,得到对应的Document.
  • 根据返回的Document注册bean

XML的验证模式

XML文件的验证模式保证了XML文件的正确性,而比较常用的验证模式有两种:DTD和XSD。

image-20200822181516391
image-20200822181600969

DTD

DTD(Document Type Dedfinition)即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可以使用的属性,可以使用的实体或符号规则。

要使用DTD验证模式的时候需要在XML文件的头部声明:

image-20200822172452919

DTD文件里面是一些ENTITY节点

image-20200822172528742

XSD

XML Schema语言就是XSD(XML Schema Definition)。Xml Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,已检查改XML文档是否符合其要求。文档设计者可以通过过XML Schema指定一个XML文档所允许的结构和内容,并可以据此检查一个XML文档是否是以有效的。XML Schema本身是一个XML文档,他符合XML语法结构,可以使用通用的XML解析器解析它。

image-20200822181110255

在使用XML Schema文档对XML实例文档进行检验,除了要声明空间外(xmlns=....),还必须指定该名称空间锁对应的XML Schema文档的存储位置。通过schemaLocation属性来指定名称空间所对一样的XML Schema文档的存储位置。它包含两个部分,一部分是名称空间的URL,另一部分就是该名称空间所表示的XML Schema文件位置或者URL地址。

获取Document

image-20200822181621954

调用了DocumentLoader接口的方法:

image-20200822181704320

这个接口只有一个实现类DefaultDocumentLoader

image-20200822182350576

BeanDefinitions

将配置文件转换为Document后,就会根据Document对象进行注册Bean.

image-20200822182535309

注册bean:

image-20200822183351796

首先通过反射获取BeanDefinitionDocumentReader的对象。

image-20200822183516072

然后记录下本次注册前,已经有多少个bean被注册了。

然后调用documentReader对象进行注册。

image-20200822183726851

使用的还是默认实现

image-20200822183745063

在使用documentReader进行读取时,首先读取的是root节点。

image-20200822183936937

接下来就是解析的核心逻辑了:

image.png

首先处理profile

image-20200822184440549

解析bean就是这里了

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