Spring——BeanFactory
Spring容器
什么是Spring容器
Spring容器是Spring的核心,它可以创建对象,把他们关联在一起,配置各个对象,并管理每个对象的整个生命周期。Spring容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为Spring Beans (一个对象就是一个Bean)。
Spring中有两种容器:
① BeanFactory 一个最简单的Spring容器,给依赖注入(DI)提供了基础的支持。
② ApplicationContext 此容器添加以一些企业需要用到的东西,更加全面。它包含了BeanFactory容器中的东西。
BeanFactory容器
在Spring中,有大量BeanFactory接口的实现类(见下图),但是,最常用的也就是XmlBeanFactory类(在Eclipse中,查看其源码可以看见已经是一个过时的类了,但我们也需要了解。),它可以从一个 XML 文件中读取配置元数据,由这些元数据来生成一个被配置化的系统或者应用。
(BeanFactory接口实现类)
注:
本文主要是针对下面一行代码执行所发生的事情的一些深入探究。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("配置文件"));
核心类介绍
Ⅰ. DefaultListableBeanFactory
XmlBeanFactory类继承自DefaultListableBeanFacotry类,而DefaultListableBeanFactory类是Bean加载的核心部分,是Spring注册及加载Bean的默认实现。
XmlBeanFactory类与DefaultListableBeanFactory类之间不同的地方就在于XmlBeanFactory类中使用了自定义的XML读取器XmlBeanDefinitionReader,
(XmlBeanDefinitionReader对象)
实现了个性化的BeanDefinitionReader读取,DefaultListableBeaFactory类继承了AbstractAutoWireCapableBeanFactory类,并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接口。
(DefaultListableBeanFactory类)
DefaultListableBeanFactory类的基类,实现接口的一些相关类图:
(容器加载部分相关类图)
XmlBeanFactory类就继承自DefaultListableBeanFactory类。XmlBeanFactory类对DefaultListableBeanFactory类进行了扩展,在XMLBeanFactory中主要使用reader属性对资源文件进行读取和注册。
XmlBeanFactory类的构造方法如下图:
(XmlBeanFactory构造方法)
Ⅱ. XmlBeanDefinitionReader
上面我们已经知道了XmlBeanFactory类和DefaultListableBeanFactory类的区别了,XmlBeanFactory类中定义了一个XmlBeanDefinitionReader对象,用于对资源文件进行处理。
XML配置文件的读取对于Spring而言非常重要,因为Spring绝大部分功能都是以配置文件作为切入点的,那么,我们就要从XmlBeanDefinitionReader类中梳理一下资源文件读取、解析和注册的大致流程。
(配置文件读取相关类)
通过上面配置文件读取相关类图可以得到读取大致流程如下:
① 通过继承自 AbstratcBeanDefinitionReader 中的方法,来使用 ResourceLoader 将资源文件路径转换为对应的 Resource 文件。
② 通过 DocumentLoader 对 Resource 文件进行转换,将 Resource 文件转换为 Document 文件。
③ 通过 DefaultBeanDefinitionDocumentReader 类对Document进行解析,并使用 BeanDefinitionParserDelegate 对 Element 进行解析。
上述三步只是读取配置文件的一个大致流程,接下来将进行更加详细的解析。
XmlBeanFactory
上面我们已经知道了 XmlBeanFacotry 和 DefaultListableBeanFactory 的区别了。XmlBeanFactory 结构如下图:
(XmlBeanFactory类结构)
在Spring中,我们创建了一个配置文件,并且在配置文件中配置了一个Bean后,那么,我们就要获取这个Bean。在测试代码中我们都写过这样一句代码:
XmlBeanFactory xmlBeanFactory = new XmlBeanFacotry(new ClassPathResource("配置文件"));
也可以是:
BeanFactory beanFactory = new XmlBeanFacotry(new ClassPathResource("配置文件"));
总之,以上两句行代码就是读取配置文件创建容器。
那么,new XmlBeanFacotry(new ClassPathResource("配置文件")) 这句代码到底是怎么回事呢?先看看下面的 XmlBeanFactory 初始化时序图吧!(画得不好,将就看吧)
(XmlBeanFactory初始化时序图)
时序图解析:
◆ 首先,时序图从一个测试类开始,这个测试类就是上面创建 XmlBeanFactory 那里。
◆ 创建 XmlBeanFactory 需要一个 Resource 对象,由于 Resource 是接口,所以我们使用其实现类 ClassPathResource 来构造 Resource 资源文件的实例对象。
◆ 有了 Resource 对象就可以进行 XmlBeanFactory 的初始化了,最后得到一个 BeanFactory。
那么,问题来了,什么又是 Resource呢?它是怎么对资源进行封装处理的呢?
● Resource 是什么
在说 Resource 之前,我们要知道一个接口:InputStreamSource,该接口封装任何能返回 InputStream 的类,比如File、Classpath下的资源、Byte、Array等。它只定义了一个方法:InputStream getInputStream() throws IOException; 该方法返回一个 InputStream 对象。
Resource 接口抽象了所有 Spring 内部所使用到的底层资源: File、URL、Classpath等。Resource 接口中的方法及大致作用如下图:
(Resource接口)
对于不同来源的资源文件都有对象的 Resource 实现:
文件(FileSystemResource)、Classpath资源(ClassPathResource)、URL资源(UrlResource)等。
(资源文件处理部分相关类)
资源文件的加载在日常开发中也经常被使用,可以直接使用 Spring 提供的类,如在加载文件时使用如下代码:
Resource resource = new ClassPathResource("资源文件");
InputStream inputStream = resource.getInputStream();
得到 inputStream 后,我们就可以按照以往的开发方式进行开发了,而且还可以使用 Resource 及其实现类的一些东西。
当通过 Resource 相关类完成了对配置文件的封装后,配置文件的读取就是 XmlBeanDefinitionReader 来完成了。
现在我们已经知道了 Spring 中将配置文件封装为 Resource 对象,下面继续了解 XmlBeanFactory 的初始化过程。从上面 XmlBeanFactory类结构图中可以看出 XMLBeanFactory 类共有两个构造方法,如下图:
(XmlBeanFactory类构造方法)
从上图我们看出,第一个构造方法内部调用了该类内部的另一个构造方法。所以,我们也就了解第二个构造方法了。
首先,在第一行出现了 super(parentBeanFactory) 这样一句代码,调用了父类(DefaultListableBeanFactory)的一个构造方法。
(DefaultListableBeanFactory有参构造方法)
来到父类构造方法,我们发现又继续调用了父类(AbstractAutowireCapableBeanFactory)的构造方法,见下图:
(AbstractAutowireCapableBeanFactory类构造方法)
从图中可以看出,有参构造方法(我们使用的就是有参构造)先调用了本类中的一个无参构造方法,无参构造方法首先执行了父类(AbstractBeanFactory)的构造方法(一个空方法),这里了解一下 ignoreDependencyInterface 方法,该方法的主要功能就是 忽略自动连接给定的依赖接口(忽略给定接口的自动装配功能)。那么,该方法有什么用?
如:当 A类 中有属性 B,当 Spring 在获取 A 的 Bean 的时候如果属性 B 还没有被初始化,Spring 就会自动初始化 B,(这也是Spring的一个重要特性)。但是,某些情况下,B 不会被初始化,比如 B 实现了 BeanNameAware 接口。Spring API介绍:应用程序上下文通常使用它来注册以其他方式解析的依赖项,如通过BeanFactoryAware实现的BeanFactory或通过ApplicationContextAware实现的ApplicationContext。默认情况下,只忽略BeanFactoryAware接口。若要忽略其他类型,请为每个类型调用此方法。
最后调用 setParentBeanFactory 方法设置 BeanFactory对象。
(setParentBeanFactory方法)
在 setParentBeanFactory 方法中有一个 if 判断,用于判断是否已经关联了 BeanFactory,如果已经关联就抛出异常。
• 加载Bean
使用 ClassPathXmlApplicationContext 类加载容器,在读取配置文件加载 Bean 的定义也就和下面的一样的了。只是前面做了些不同的处理。
在上面讲到 Resource 时,我们知道了 XmlBeanFactory 的构造方法,我们也知道了其中一个构造方法首先调用了父类的构造方法,那么,在super()语句下面就是 this.reader.loadBeanDefinitions(resource) 方法的调用。这个方法才是整个资源加载的切入点,下面是该方法调用的时序图:
(loadBeanDefinitions方法执行时序图)
从上图可以看到,这个方法的调用引起了很大一串的工作。然而这些工作也只是在做准备工作,下面说说这里究竟在准备什么工作:
(1)封装资源文件。当调用 loadBeanDefinitions 方法时,就会跳转到该方法中,该方法就调用了本类的一个重载方法,同时根据 Resource 对象创建一个EncodedResource 对象作为参数传递,使用 EncodedResource 的作用就是把 Resource 使用 EncodedResource 类进行封装。
(loadBeanDefinitions(Resource resource)方法)
(2)获取输入流构建 inputSource。从 Resource 中获取对应的 InputStream 并创建 InputSource。
(loadBeanDefinitions(EncodedResource encodedResource)方法中 获取 InputStream 并 创建 InputSource)
(3)通过刚刚创建的 InputSource 对象和 Resource继续调用 doLoadBeanDefinitions()方法。
(doLoadBeanDefinitions(InputSource, Resource)方法调用)
上面,我们多次看到 EncodedResource ,那么,它到底是什么?
• EncodedResource
通过名字可以猜测该类和编码相关。该类主要就是对资源文件的编码进行处理的。其中一个很重要的方法 getReader() 方法 ,当设置了编码属性时,Spring 就会使用相应的编码作为输入流的编码。
首先看看它的一个构造方法:(该类一共有四个构造方法,但其余三个构造方法均调用了下面这个构造方法)
(EncodedResource类的一个构造方法)
该构造方法主要就是对类中的属性进行初始化。
再来看看 getReader() 方法:
(getReader() 方法)
getReader() 方法构造了一个含编码的 InputStreamReader。将 Resource 封装为 EncodedResource 对象后,就来到了 XmlBeanDefinitionReader 类中的 loadBeanDefinitions() 方法(另一个重载后的方法),也就是下图中的方法。
(loadBeanDefinitions(EncodedResource ..)方法部分重要代码)
上图方法才算是真正的数据准备,也就是往上第7张 时序图中所描述的部分。
再次回顾以上数据准备部分内容,首先将传入的 Resource 对象封装为 EncodedResource,为什么需要封装?目的是考虑到 Resource 可能存在编码要求的情况,其次,通过SAX读取XML文件的方式来准备 InputSource对象,最后将准备的数据通过参数传递给核心处理方法 doLoadBeanDefinitions(InputSource inputSource, Resource resource)。下面就来看看 doLoadBeanDefinitions() 方法做了什么事情:
(doLoadBeanDefinitions() 方法部分代码(除catch部分))
doLoadBeanDefinitions() 方法 try 后面有多个 catch ,除开这些 catch,那么,这段代码做了以下三件事情:
(1)获取对 XML 文件的验证模式
(2)加载 XML 文件,得到对应的 Document 对象
(3)根据返回的 Document 对象注册 Bean 信息
以上三个操作支撑着整个Spring容器的实现基础,下面就将从这三个步骤讲起。
• 获取 XML的验证模式
XML 验证模式的作用:用于保证 XML 文件的正确性(貌似就是所说的约束),常用的验证模式有两种:DTD(Document Type Definition) 和 XSD(XML Schemas Definition)。详细了解验证模式请自行上网搜索。
在上一张(doLoadBeanDefinitions方法代码)图中,我们看见 try 块中第一行代码是: Document document = doLoadDocument(inputSource, resource); 调用了本类中的 doLoadDocument() 方法,但是,这句代码主要是用来获取 Document对象的,也就是上面第二件事情;但是,在其中会先完成对 XML 文件验证模式的获取。
(doLoadDocument() 方法)
我们可以从上图看到,在 loadDocument() 方法执行时,先执行了 getValidationModeForResource(resource) 方法,该方法返回一个 int 类型的值。(在 Spring3.2 中,并不存在 doLoadDocument() 方法,是直接在 doLoadBeanDefintions() 方法中调用 getValidationModeForResource(resource) 方法 和 loadDocument() 方法),下面我们看看 getValidationModeForResource(resource) 方法做了什么事情:
(getValidationModeForResource(resource) 方法)
在 上面方法中,使用了几个常量,下图是几个常量所表示的值:
(org.springframework.util.xml.XmlValidationModeDetector类中的几个常量)
注意:XmlBeanDefinitionsReader 类中也有上图中除了 DOCTYPE 常量以外的几个 int 类型的同名的常量,其值就是上面的值。也就是 Spring 把不同的验证模式使用了不同数值表示了而已。
在往上第二张图中得知 getValidationModeForResource(resource) 方法首先判断是否手动指定(通过 setValidationMode() 方法设置验证模式)了验证模式,判断方式就是 获取 validationMode 属性进行判断。否则使用自动检测的方式,自动检测调用了 org.springframework.beans.factory.xml.XmlBeanDefinitionReader.detectValidationMode(resource) 方法(本类中的方法)实现。
(XmlBeanDefinitionReader.detectValidationMode(resource) 方法 省略了catch处理部分代码)
在上图最后一个 try 块调用了 XmlValidationModeDetector 类中的 detectValidationMode(inputStream) 方法做进一步处理,下面就看看这个方法(这里如果要全面了解,建议自己查看一遍源码,毕竟在该方法中还调用了其他方法,也使用了几个常量,这里并没有列出)。
(XmlValidationModeDetector 类中的 detectValidationMode(inputStream) 方法)
上面获取验证模式部分需要根据 DTD 和 XSD来进行理解,因为获取验证模式就是根据两种验证模式使用方法来的。Spring 检测验证模式的方法就是判断是否包含 DOCTYPE,如果包含就是 DTD,否则就是 XSD(这一点从上面一张图的第一行注释就是可以看出:查看文件以查找 DOCTYPE),这一点从上图方法中可以很容易的看出。
到这里,获取验证模式就讲解完了。
• 获取 Document
上面我们知道了在获取 Document 之前要先获取 XML 验证模式。下面我们就来看看 Spring 中是怎么获取 Document 的。在上面 (doLoadDocument() 方法)图中我们看见 doLoadDocument 方法调用了本类 documentLoader 的 loadDocuemnt() 方法。documentLoader 定义如下:
private DocumentLoader documentLoader = new DefaultDocumentLoader();
DocumentLoader 是一个接口,所以使用其实现类 DefaultDocumentLoader;
先来看看 DefaultDocumentLoader 类中的 loadDocument() 方法吧。
(loadDocument() 方法)
上面这段代码就是基本的 通过 SAX 解析 XML,这里算是基本步骤了。首先创建 DocumentBuilderFacotry 对象,再通过 DocumentBuilderFactory 创建 DocumentBuilder,然后解析 inputSource 来返回 Document 对象。这里涉及到了 XML 解析相关知识,可自行上网深入了解。
• 解析及注册 BeanDefinitions
在上面 (doLoadBeanDefinitions() 方法) 图中我们知道 获取到了 Document 后,就执行下面这行代码:
return registerBeanDefinitions(doc, resource);
也就是 继续调用 registerBeanDefinitions(doc, resource) 方法。
(registerBeanDefinitions(doc, resource) 方法)
上图中第一行代码就是创建 BeanDefinitionDocumentReader,BeanDefinitionDocumentReader 是接口,而实例化是在 createBeanDefinitionDocumentReader() 方法中完成的,而通过执行此方法后,BeanDefinitionDocumentReader 真正的类型就是 DefaultBeanDefinitionDocumentReader (它的实现类)了。
上图中第三行就是加载、注册 Bean了,由于 BeanDefinitionDocumentReader 是接口,所以我们来到 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions() 方法。
(registerBeanDefinitions(Document, XmlReaderContext) 方法)
上图方法的重要目的之一就是提取 root,再将 root 作为参数继续 BeanDefinition 的注册。
(doRegisterBeanDefinitions(Element) 方法)
上图代码中涉及到 profile 属性,该属性详情还请自行上网了解。上图程序第二部分,程序会先获取 beans 节点是否定义了 profile 属性,如果定义了则需要到环境变量中区寻找,每定义就不解析。
处理了 profile 就可以开始进行 XML的读取了,下面看看上图框住的方法 parseBeanDefinitions(root, this.delegate)。
(parseBeanDefinitions(Element, BeanDefinitionParserDelegate) 方法)
在 Spring 的 XML文件中可以使用默认的 Bean声明,也可以自定义。所以 Spring 针对不同的 Bean 声明做了不同的处理。
关于 bean 标签的解析将在后面介绍。直到这里,以上内容也只是 Spring 在加载配置文件,还没有真正的开始解析 bean标签。只是 Spring 容器的一个介绍。
PS: 本文存在诸多不足之处,望指出!
Spring——BeanFactory的更多相关文章
- Spring BeanFactory实例化Bean的详细过程
Spring中Bean的实例化是Bean生命周期的一个重要环节,通常Bean初始化后将不再改变. 那么Spring实例Bean的过程到底是怎么样的呢?! 要想获取到一个bean对象,得先通过Bea ...
- spring BeanFactory及ApplicationContext中Bean的生命周期
spring bean 的生命周期 spring BeanFactory及ApplicationContext在读取配置文件后.实例化bean前后.设置bean的属性前后这些点都可以通过实现接口添加我 ...
- Spring BeanFactory与FactoryBean的区别及其各自的详细介绍于用法
Spring BeanFactory与FactoryBean的区别及其各自的详细介绍于用法 1. BeanFactory BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于 ...
- Spring BeanFactory 与 FactoryBean 的区别
BeanFactory 和 FactoryBean 都是Spring Beans模块下的接口 BeanFactory是spring简单工厂模式的接口类,spring IOC特性核心类,提供从工厂类中获 ...
- Spring 源码(3)Spring BeanFactory 是怎么创建的?
Spring创建 BeanFactory 的方式 按照Bean的配置方式手动创建可以分为两种: 使用XMl配置的Bean 这种方式使用xml配置文件配置Bean的信息并且设置扫描的路径,扫描到的包可以 ...
- 攻城狮在路上(贰) Spring(四)--- Spring BeanFactory简介
BeanFactory时Spring框架最核心的接口,它提供了高级IoC的配置机制,使管理不同类型的Java对象成为了可能.我们一般称BeanFactory为IoC容器.BeanFactory是Spr ...
- spring BeanFactory概述
BeanFactory是Spring提供的两种容器类型之一,它是基础的IoC容器,并提供完整的IoC服务支持.如果没有指定,默认采用延迟初始化策略.只有当客户端对象需要访问容器中的某个受管对象的时候, ...
- Spring BeanFactory源码学习
一.BeanFactory BeanFactory是Spring IOC容器的基础,是IOC容器的基础接口,所有的容器都是从它这里继承实现而来.BeanFactory提供了最基本的IOC容器的功能,即 ...
- Spring BeanFactory getBean 源码剖析
首先看一张时序图 最开始,一切都来自这里: public static void main(String[] args) { ApplicationContext context = new Clas ...
- Difference between BeanFactory and FactoryBean in Spring Framework (Spring BeanFactory与Factory区别)
参见原文:http://www.geekabyte.io/2014/11/difference-between-beanfactory-and.html geekAbyte Codes and Ran ...
随机推荐
- php phppowerpoint
今天早上从订阅的 Zend DevZone 看到篇很有意思的文章. Creating PowerPoint 2007 files using PHP. 试了一下. 果然很又意思, 分享给大家吧. 程序 ...
- Java数组去重(利用数组,不借助集合)
今天有个同学问我说老师,Java里边数组怎么去重,在不借助List集合的情况下,最后呢我整理了一下,打算发一篇博文,希望能帮助到有用的人,大佬绕过 public static void arrUniq ...
- RHEL6.5安装QT5.4,设置环境变量
qt5.4.run在[/home/share]目录下 vim ~/.bashrcexport PATH=/opt/oracle/Qt5.4.1/Tools/QtCreator/bin:/opt/ora ...
- phpstorm如何配置xdebug?(hpStudy+PhpStorm+XDebug配置)
xdebug是什么? 初次接触,反复试了几次终于把这个xdebug给搞清楚了,类似于前端的控制台这样的东西,可以根据断点展示我们想要看的数据. 如何配置xdebug? 配置前说明: 1.phpStud ...
- fcc 响应式框架Bootstrap 练习2
text-primary 属性值使标题直接变成了红色,text-center使标题直接居中 <h2 class="text-primary text-center"> ...
- brew update失败提示:/System/Library/Frameworks/Ruby.framework/的解决方法
本文由@ray 出品,转载请注明出处. 文章链接:http://www.cnblogs.com/wolfray/p/8040701.html 想用brew安装wget,但是提示失败,然后想先 bre ...
- PHP魔术法__set和__get
__set: 在给不可访问属性赋值时,__set() 会被调用.语法如下: public void __set ( string $name , mixed $value ) __get: 读取不可访 ...
- Android开发笔记(9)——初步设置Menu
转载请注明:http://www.cnblogs.com/igoslly/p/6858656.html 初步设置Menu 设置Menu,在ActionBar上添加按钮操作: 在 ...
- SQL基本操作——约束
我们将主要探讨以下几种约束: 1.NOT NULL 2.UNIQUE 3.PRIMARY KEY 4.FOREIGN KEY 5.CHECK 6.DEFAULT SQL NOTNULL约束:NOT N ...
- [Windows Server 2012] MySQL安全加固
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:MySQL ...