Spring IOC/BeanFactory/ApplicationContext的工作流程/实现原理/初始化/依赖注入源码详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotpots/article/details/78046943

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

最后 综合 过程:

 

猜你喜欢

转载自blog.csdn.net/hotpots/article/details/78046943