Spring IOC/BeanFactory/ApplicationContext的工作流程/实现原理/初始化/依赖注入源码详解
Spring的工作流程/实现原理之基石IOC/BeanFactory/ApplicationContext 更新1:2017/11/23更新2:2018/1/30(截图)一、什么是IOC容器? IOC(Inversion of Control)、控制反转亦称依赖注入.IOC容器指的是实现对依赖对象的创建(无参构造器)、管理(参数注入)、销毁(关闭BeanFactory).二、Spring IOC
Spring的工作流程/实现原理之基石IOC/BeanFactory/ApplicationContext
更新1:2017/11/23
更新2:2018/1/30(截图)
一、什么是IOC容器?
IOC(Inversion of Control)、控制反转亦称依赖注入.IOC容器指的是实现对依赖对象的创建(无参构造器)、管理(参数注入)、销毁(关闭BeanFactory).
二、Spring IOC 的两种实现方式(接口)?
1. BeanFactory接口
org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了IoC容器最基本功能(工厂模式).(如图)
概述:
其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为.
1、BeanFactory实现:
步骤1、BeanFactory(继承的是DefaultListableBeanFactory),提供基本的IoC容器功能,可以从classpath或文件系统等获取资源;
步骤2、利用ClassPathResource
可以从classpath中读取XML文件
Resource cr = newClassPathResource("applicationContext.xml");
Resource resource = newFileSystemResource(“beans.xml”);
BeanFactory beanFactory = newXmlBeanFactory(resource);
2、 ApplicationContext接口(扩展了BeanFactory)
而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。
BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。
ApplicationContext实现:
1、 ClassPathXmlApplicationContext(继承了抽象类):,从classpath获取配置文件;
BeanFactory beanFactory = newClassPathXmlApplicationContext("classpath.xml");
2、FileSystemXmlApplicationContext:从文件系统获取配置文件。
BeanFactory beanFactory = newFileSystemXmlApplicationContext("fileSystemConfig.xml");
3.利用XmlWebApplicationContext读取
XmlWebApplicationContext ctx = newXmlWebApplicationContext();
概述:ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。
从ApplicationContext接口的实现,我们看出其特点(比起BeanFactory):
1. 支持信息源,可以实现国际化。(实现MessageSource接口)
2. 访问资源。(实现ResourcePatternResolver接口)
3. 支持应用事件。(实现ApplicationEventPublisher接口)
4. 提供附加服务(更面向框架的使用风格)
三、Spring IOC的实现流程(bean对象是怎么创建出来的)?
博主对源码进行深入的理解
总结如下:
1、准备配置文件:在项目中使用Spring(初始化/引入配置文件)
在配置文件中声明Bean定义也就是为Bean配置元数据。
方式1. 直接加载ApplicationContext
ApplicationContext ctx = newClasspathXmlApplicationContext("applicationContext.xml");
方式2. 使用ContextLoaderListener
从ServletContext取得web.xml中初始化的ApplicationContext
在web.xml中配置listener
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath: ApplicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
解释:
org.springframework.web.context.ContextLoaderListener
实现了 javax.servlet.ServletContextListener接口。ServletContextListener接口能够监听ServletContext对象的生命周期,因为每个web应用仅有一个ServletContext对象,故实际上该接口监听的是整个web应用。
方式3.使用 AnnotationConfigApplicationContext 注册配置类
1、使用 AnnotationConfigApplicationContext 注册 AppContext 类
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppContext.class);
Course course = ctx.getBean(Course.class);
course.getName();
}
正如以上代码所示,AppContext 配置类的注册方式是将其传递给 AnnotationConfigApplicationContext 构造函数。此外,您还可以使用所述上下文类的 register 方法来注册配置类。以下代码展示了另外一种方法。
2、注册 AppContext 类:另外一种方法
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppContext.class)
}
注册配置类将自动注册 @Bean 注释的方法名称,因而其对应的 bean 就是 Course、Module 和 Assignment。随后您可以使用 getBean 方法来获取相关的 bean,并调用其业务方法。如您所见,编写 Java 的配置类并将其注册到 Spring 上下文非常简单。下一节将讨论如何将基于 Java 的配置与 Web 应用程序配合使用。
2、定位(BeanBefinition的Resource)
Resource是Sping中用于封装I/O操作的接口。在创建spring容器时,通常要访问XML配置文件(步骤1),除此之外还可以通过访问文件类型、二进制流等方式访问资源,还有当需要网络上的资源时可以通过访问URL,Spring把这些文件统称为Resource,Resource的体系结构如下:
常用的resource资源类型如下:
FileSystemResource:以文件的绝对路径方式进行访问资源,效果类似于Java中的File;
ClassPathResourcee:以类路径的方式访问资源,效果类似于this.getClass().getResource("/").getPath();
ServletContextResource:web应用根目录的方式访问资源,效果类似于request.getServletContext().getRealPath("");
UrlResource:访问网络资源的实现类。例如file: http: ftp:等前缀的资源对象;
ByteArrayResource: 访问字节数组资源的实现类。
那如何获取上图中对应的各种Resource对象呢?
Spring提供了ResourceLoader接口用于实现不同的Resource加载策略,该接口的实例对象中可以获取一个resource对象,也就是说将不同Resource实例的创建交给ResourceLoader的实现类来处理。ResourceLoader接口中只定义了两个方法:
1、ResourcegetResource(Stringlocation); //通过提供的资源location参数获取Resource实例
他的顶层接口是:
public interfaceResource extendsInputStreamSource {
boolean exists(); // 资源是否存在
boolean isReadable(); // 资源是否可读
boolean isOpen(); // 资源所代表的句柄是否被一个stream打开了
URL getURL() throws IOException; // 返回资源的URL的句柄
URI getURI() throws IOException; // 返回资源的URI的句柄
File getFile() throws IOException; // 返回资源的File的句柄
long contentLength() throwsIOException; // 资源内容的长度
long lastModified() throws IOException; // 资源最后的修改时间
Resource createRelative(String relativePath)throws IOException; //根据资源的相对路径创建新资源
String getFilename(); // 资源的文件名
String getDescription(); //资源的描述
}
2、ClassLoadergetClassLoader(); // 获取ClassLoader, ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流等)、通过ClassLoader可将资源载入JVM获取. (当JVM需要某类时, ClassLoader.loadClass(Stringname)方法返回Class对象创建实例)
注:ApplicationContext的所有实现类都实现RecourceLoader接口,因此可以通过直接调用getResource(参数)获取Resoure对象。不同的ApplicatonContext实现类使用getResource方法取得的资源类型不同,例如:FileSystemXmlApplicationContext.getResource获取的就是FileSystemResource实例;ClassPathXmlApplicationContext.gerResource获取的就是ClassPathResource实例;XmlWebApplicationContext.getResource获取的就是ServletContextResource实例。
结果:在资源定位过程完成以后,就为资源文件中的bean的载入创造了I/O操作的条件,如何读取资源中的数据将会在下一步介绍的BeanDefinition的载入过程中描述。
3、载入(以FileSystemXmlApplicationContext载入为例)
BeanDefinition与Resource的联系呢?
/**
* Load bean definitions from the specifiedresource.
* @param resource the resource descriptor
* @return the number of bean definitionsfound
* @throws BeanDefinitionStoreException incase of loading or parsingerrors
*/
intloadBeanDefinitions(Resourceresource) throws BeanDefinitionStoreException;
总之,BeanDefinition相当于一个数据结构,这个数据结构的生成过程是根据定位的resource资源对象中的bean而来的(loadBeanDefinitions方法),这些bean在Spirng IoC容器内部表示成了的BeanDefintion这样的数据结构,IoC容器对bean的管理和依赖注入的实现都是通过操作BeanDefinition来进行的。
载入过程把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition,在IOC容器中是通过ConcurretHashMap来保持和维护和获取的。总地说来,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,这个BeanDefinition定义了一系列的数据来使得IoC容器能够方便地对POJO对象也就是Spring的Bean进行管理。即BeanDefinition就是Spring的领域对象。
1.设置资源加载器和资源定位
2.AbstractApplicationContext的refresh函数载入Bean定义过程:
3.AbstractApplicationContext子类的refreshBeanFactory()方法:
4.AbstractRefreshableApplicationContext子类的loadBeanDefinitions方法:
5.AbstractBeanDefinitionReader读取Bean定义资源:
6.对BeanDefinition载入的实现
7.资源加载器获取要读入的资源:
8.XmlBeanDefinitionReader加载Bean定义资源:
9.DocumentLoader将Bean定义资源转换为Document对象:
10.XmlBeanDefinitionReader解析载入的Bean定义资源文件:
11.DefaultBeanDefinitionDocumentReader对Bean定义的Document对象解析:
12.BeanDefinitionParserDelegate解析Bean定义资源文件中的<Bean>元素:
13、BeanDefinitionParserDelegate解析<property>元素:
14、解析<property>元素的子元素:
15、解析<list>子元素:
注:BeanDefinition的载入分成两部分(XML的解析器和documentReader解析):
首先通过调用XML的解析器得到document对象,但这些document对象并没有按照Spring的Bean规则进行解析。
在完成通用的XML解析以后,才是按照Spring的Bean规则进行解析的地方,这个按照Spring的Bean规则进行解析的过程是在 documentReader中实现的。
这里使用的documentReader是默认设置好的DefaultBean-DefinitionDocumentReader。
这个DefaultBeanDefinitionDocumentReader的创建是在后面的方法中完成的,然后再完成BeanDefinition的处理,处理的结果由BeanDefinitionHolder对象来持有。
这个BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition的使用相关的信息,比如Bean的名字、别名集合等。这个BeanDefinition-Holder的生成是通过对Document文档树的内容进行解析来完成的,可以看到这个解析过程是由BeanDefinition-ParserDelegate来实现(具体在processBeanDefinition方法中实现)的,同时这个解析是与Spring对BeanDefinition的配置规则紧密相关的。
4、注册
这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的,这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。可以看到,在IoC容器内部,是通过使用一个HashMap来持有这些BeanDefinition数据的。
BeanDefinition加载完成之后,要把它注册到IoC容器中,其实就是把bean对象添加到一个HashMap中,BeanDefinitionReaderUtils.registerBeanDefinition方法完成注册。
这里getReaderContext得到的readerContext是之前步骤中已经设置好的,即DefaultListableBeanFactory,他实现了BeanDefinitionRegistry的方法registerBeanDefinition,最终完成BeanDefinition的注册。
完成了上面的三步后,目前ApplicationContext中有两种类型的结构,一个是DefaultListableBeanFactory,它是Spring IOC容器,另一种是若干个BeanDefinitionHolder,这里面包含实际的Bean对象,AbstractBeanDefition。
需要把二者关联起来,这样Spring才能对Bean进行管理。在DefaultListableBeanFactory中定义了一个Map对象,保存所有的BeanDefition。这个注册的过程就是把前面解析得到的Bean放入这个Map的过程。
registerBeanDefinition
注册的入口,对于普通的Bean和Alias调用不同类型的注册方法进行注册。
registerBeanDefinition
注册Bean 定义在DefaultListableBeanFactory中
registerAlias
定义在SimpleAliasRegistry类,对别名进行注册
5、依赖注入
这个过程是Bean创建出来的过程,在大多数情况下,Spring容器直接通过new关键字调用构造器来创建Bean实例,而class属性指定Bean实例的实现类,但这不是实例化Bean的唯一方法。实际上,Spring支持使用以下三种方式来创建Bean:
(1)调用构造器创建Bean
(2)调用静态工厂方法创建Bean
(3)调用实例工厂方法创建Bean
最后 综合 过程:
更多推荐
所有评论(0)