zoukankan      html  css  js  c++  java
  • Spring----Spring的IoC容器

     一. Spring提供了两种容器类型:BeanFactory和ApplicationContext

    BeanFactory

    ApplicationContext

    总结来说,实现BeanFactory接口的简单容器系列,只实现了容器的最基本功能;而ApplicationContext作为容器的高级形态存在,增加了许多面向框架的特性,同时对应用环境做了许多适配。

    IoC接口容器设计图:

    可以看到BeanFactory是IoC容器的最基本接口。

    BeanFactory接口设计了getBean方法,这个方法是IoC容器API的主要方案分,通过这个方法,可以获取IoC容器中的Bean,Bean的取得是通过指定名字来索引的。

    对于头一次学Spring的人来说,直接看源码肯定会头大,所以我就尽我的理解来简单说一下,如果说的不对,就请大家指正。

    主要的参考是:《Spring技术内幕》、《Spring揭秘》、《Spring实战》

    二. IoC容器的初始化过程

    首先,要想知道如何使用IoC容器,就需要知道IoC容器是如何启动的,或者说IoC是如何初始化的,只有初始化了才能进行使用,才有后续。

    IoC容器的初始化是有refresh()方法来启动的,这个方法标志着IoC容器的正式启动。

    这个启动包括:BeanDefinition的Resource定位、载入和注册三个过程。

    Resource 定位:这是一个通用的数据来源对象,也就是说先要找到资源

    载入数据:将找到的数据进行载入到内存,形成POJO 对象,这里是定义的BeanDefinition 对象,它会装载我们需要的数据

    注册:向IoC容器注册这些BeanDefinition的过程。

    (参考:https://greemranqq.iteye.com/blog/2020653

    1.定位:

    a.Resource的定位:

    Resource接口:该接口的目的是统一资源,也就是说无论是网络、本地文件等上的资源,最终都会以Resource 的形式获得。

    拿吗这个资源是什么资源,实际上常用的资源有四个:

    参考:https://blog.csdn.net/u013309230/article/details/51559444

    ClassPathResource:通过 ClassPathResource 以类路径的方式进行访问;
    FileSystemResource:通过 FileSystemResource 以文件系统绝对路径的方式进行访问;
    ServletContextResource:通过 ServletContextResource 以相对于Web应用根目录的方式进行访问;
    UrlResource :通过java.net.URL来访问资源,当然它也支持File格式,如“file:”。

    Resource是spring访问资源最基本的接口。实际访问的时候直接用Resource接口就可以,不必使用其子类。

    其实经常用到的(resource的真正目的)方法是public InputStream getInputStream()。

    1 public InputStream getInputStream() throws IOException {  
    2              return new FileInputStream( this. file);  
    3       }  

     

    b.Resource加载器

    通过Resource接口来获得的资源,并不能直接被使用,而是需要进行一步加载的操作。

    ResourceLoader接口:这里会通过Resource getResource(String location),来获得资源,然后在再让各种装载器去加载。

     DefaultResourceLoader 实现类:对ResourceLoader的实现(对于Default的实现类,我个人感觉都是Spring默认的功能相对完善的实现类,也是使用较多的(有待考究))

    c.Resource读取器

    加载完了就可以对资源进行读取。这里的Resource不是直接DefaultListableBeanFactory(是很重要的一个IoC实现,也是默认功能较为完善的一个BeanFactory)直接使用,

    Spring通过BeanDefinitionReader来对这些信息进行处理。

    简单概括为:

    BeanDefinitionReader接口:提供对资源装载的基本方法接口

    AbstractBeanDefinitionReader 抽象类:对上面接口的抽象实现,这里要求具体的实现类进行操作

    XmlBeanDefinitionReader 实现类:我们这里以xml 的形式进行实现,当然还有properties方式对资源的读取,也就是loadBeanDefinitions 加载都在这里进行 。

    注:到这一步时:在ApplicationContext中,Spring已经为我们提供了一系列加载不同的Resource的读取器的实现;而DefaultListableBeanFactory只是一个纯粹的IoC容器,

    需要为它配置特定的读取器(比如Xml的读取器或者properties的或者url等等)才能完成这些功能。

    2.载入---解析(载入过后还有解析):

    对于IoC容器来说,这个载入过程,就相当于把读取到的资源在IoC容器中转化成一个Spring内部表示的数据结构的过程。

    a.解析成为document对象

    BeanDefinition的载入分成两部分,首先通过调用xml解析器(就以xml资源为例)得到document对象,但是这些document对象并没有按照Spring的Bean规则进行解析。

    xml(properties) 解析:这里在XmlBeanDefinitionReader 进行实现的,它会将资源信息,解析成我们需要的Document 对象

    DocumentLoader 接口:定义基本解析方法

    DefaultDocumentLoader 实现:对资源进行处理,返回Document 对象

    b.document解析成为BeanDefinition

    在完成xml解析之后,会按照Spring的Bean规则再一次进行解析,这个解析过程实在documentReader中实现的。

    registerBeanDefinitions: DefaultDocumentLoader 返回的的document对象进入到该方法中,,具体过程是由BeanDefinitionDocumentReader 来完成的

    BeanDefinitionDocumentReader 接口:定义了解析方法

    DefaultBeanDefinitionDocumentReader 实现:基本方法的实现,完成解析和注册功能,这里的解析功能是让BeanDefinitionParserDelegate 处理,注册是让另外的处理

    BeanDefinitionParserDelegate :这个类是专门解析Document 对象,然后完成注册功能

    DefaultBeanDefinitionDocumentReader完成处理后,处理的结果由BeanDefinitionHolder对象来持有。这个BeanDefinitionHolder除了持有BeanDefinition对象之外,还持有

    其他与BeanDefinition的使用相关的信息,比如Bean的名字、别名集合等。[ id,name属性放入List aliases中;aliases持有所有别名,BeanDefinitionParserDelegate中属性 Set usedNames 会持有所有解析出来的beanName 和 aliases]

    (BeanDefinitionHolder的生成是通过对Document文档树的内容进行解析来完成的,它的实现由BeanDefinitionParserDelegate来完成,得到这个BeanDefinitionHolder就意味着BeanDefinition

    是通过BeanDefinitionParserDelegate对xml元素信息按照spring的Bean规则进行解析得到的)

    BeanDefinitionHolder里面不但有BeanDefinition对象,还有一些其他的相关信息,那么这个信息又是怎样被放到里面的呢?

    比如,在xml文件中,处理xml文件中的<bean></bean>这个元素信息,各种Bean的属性配置等等,是通过parseBeanDefinitionElement来完成的。

    注:beanClass、description、lazyInit这些属性都集中在这里

    总结一下,就是通过两次解析,得到了我们IoC容器所需要的那种BeanDefinition

    经过解析后,xml文件中定义的bean(或者说我们自己定义的不符合IoC容器规范的BeanDefinition)就被载入到了IoC容器中,并在容器中建立了数据映射。

    在IoC容器中建立了对应的数据结构,或者可以说看成是POJO对象在IoC容器中的抽象,这些数据以AbstractBeanDefinition为入口,让IoC容器执行索引、查询和操作。

    现在IoC容器中存在的还只是一些静态的配置信息,IoC容器要想起作用,就需要进行注册。

    3.注册

    DefaultListableBeanFactory IOC容器:这里其实很简单,仅仅对刚才获得的BeanDefinition放到 一个ConcurrentHashMap里面,就完成注册了。

    1 private final Map<String, BeanDefinition> beanDefinition = new ConcurrentHashMap<String, BeanDefinition>();

    具体来说,在DefaultListableBeanFactory 实现了BeanDEfinitionRegistry的接口,这个接口的实现类,完成了注册的过程。

    三. IoC容器的依赖注入

    当IoC容器完成了初始化,这个过程的主要作用是在IoC容器中建立BeanDefinition数据映射。在这个过程中,并没有看到IoC容器对Bean依赖关系进行注入,也就是说,

    作为IoC容器,他的依赖注入功能我们还不知道。

    这一部分就和Bean的生命周期交叉在一起来理解。

    依赖注入发生在用户第一次向IoC容器索要Bean时触发,当然了,这也不是绝对的,lazy-init属性可以完成容器对Bean的预初始化。

    IOC 注入一般发生在getBean 阶段:

    当用户向IoC容器索要Bean的时候,会发现BeanFactory接口中有一个getBean方法;在DefaultListableBeanFactory 实现类中的基类AbstractBeanFactory实现了getBean方法。

    getBean方法中调用doGetBean方法,也就是说doGetBean是实际取得Bean的地方,也就是依赖注入发生的地方。

    AbstractBeanFactory :这个基类里面会有getBean 的实现。这里的取值方式从:缓存(那些已经被创建过的单例模式的Bean,不需要重复创建)-->工厂-->双亲工厂(链)  进行获取,同时会递归获取所有依赖的bean.

    AbstractAutowireCapableBeanFactory: 这个类是createBean(顾名思义:创建Bean方法)的方法所在,当然里面通过各种解析,最终都由InstantiationStrategy 接口的 instantiate 进行实例化

    InstantiationStrategy 接口:提供了instantiate 方法,提供了反射(实现类SimpleInstantiationStrategy )和Cglib(实现类CglibSubclassingInstantiationStrategy)的生成方式

    SimpleInstantiationStrategy 实现类:这里主要是反射,通过构造器生成对象

    CglibSubclassingInstantiationStrategy实现类:继承了上面的类,这里通过cglib 进行生成。

    实例化Bean对象后,我们看一下Bean声明周期的第一步:

    是不是就理解了这个东西是个啥。

    bean 实例化后,就会对他们之间的关系进行设置,完成整个依赖注入的过程。这个过程涉及对更重Bean对象的属性的处理过程(即依赖关系处理的过程),

    这些依赖关系处理的依据就是已经解析得到的BeanDefinition。这里需要回到前面的populateBean方法,也就是------------------------>

    还是会回到AbstractAutowireCapableBeanFactory,因为它会调用上面的类,实例化bean,同时组装依赖关系还是在里面进行。

    1.populateBean 方法:这个方法包会解析BeanDefintion中Property 的值,然后调用applyPropertyValues方法,对获得的property 放到我们BeanWrapper 中,

    进行具体的注入过程会在实现类BeanWrapperImpl 进行,会进行各种属性值的设定。

    2.BeanDefinitionValueResolver :从名字可以看出,这个类是解析BeanDefinition关系的具体实现,上面的方法的解析,就是调的这个类。

    这里会对Reference、set等 包含这些属性的进行解析,准备下面的注入。

    3.BeanWrapperImpl:上面的都是准备,这里就会完成真正的属性注入。这里主要会调用setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)方法,

    通过getPropertyValue中获得的属性引用,进行赋值操作。这就完成了整个注入过程。

    在完成注入后,我们再来看一下Bean的生命周期第二步:

    就很好理解Bean生命周期的前两步了。

    四. Bean对IoC容器的感知

    (具体在《Spring揭秘》中有完整的Bean的生命周期讲解,我就不在这里很具体的说了)

    容器管理的Bean一般不需要了解容器的状态和直接使用Bean,但是在某些情况下,是需要在Bean中直接对IOC容器进行操作的,

    这时候,就需要在Bean中设定对容器的感知。Spring IOC也提供了该功能,它是通过特定的aware接口来完成的。aware接口有如下这些:

    BeanNameAware:可以在Bean中得到它在IOC容器中的Bean的实例名称

    BeanFactoryAware:可以在Bean中得到Bean所在的IOC容器,从而直接在Bean中使用IOC容器的服务

    ApplicationContextAware:可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务

    MessageSourceAware:在Bean中可以得到消息源

    ApplicationEventPublisherAware:在Bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件

    ResourceLoaderAware:在Bean中可以得到resourceloader,从而在Bean中使用ResourceLoader加载外部对应的Resource资源

    这个地方就是Bean生命周期的3.4步,当然,这两步针对的是BeanFactory,如果是ApplicationContextAware接口,

    会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,也就是在这两步之后再加一步。

    最后总结:

    1. Spring的IoC容器是包含了IoC Service Provider功能的更高级的容器。

    2. IoC容器初始化有三步:Resource定位、载入(与解析)、注册。

    3. IoC容器初始化后,在用户使用时,会对Bean进行依赖注入,实例化Bean。

    4. Bean的整个生命周期都是由IoC来进行管理的,包括使用和销毁。

    最后最后,我把《Spring揭秘》这本书(我是没买到,好像绝版了)的PDF发给大家:

    https://pan.baidu.com/s/1AUkbbMXEecRhv6waPgUbVw

    提取码:xobf

     

  • 相关阅读:
    SVN 常用keywords 记录
    HTML5新特性介绍
    php文件上传错误代码
    MySQL的 Grant命令权限分配
    前端开发工具整理
    Java多线程编程经验谈
    一套密码强度判断方案
    傲游浏览器下Flash和Js交互问题
    在xml中使用&和字符
    ibatis和myibatis
  • 原文地址:https://www.cnblogs.com/levontor/p/11042547.html
Copyright © 2011-2022 走看看