Spring IOC设计原理解析
Posted dongwang911
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring IOC设计原理解析相关的知识,希望对你有一定的参考价值。
一、 什么是Ioc/DI?
大家都知道IOC 是控制反转(Inversion of Control,缩写为IoC)是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。 之前我们的bean对象的创建是交由调用者通过new 的方式进行创建的, 那么这样的方式有一些不足之处: bean的整个生命周期都交给了这个调用者了, 过程如下:并将该对象的引用返回。如果该返回被一个引用接收,由于对象的引用不为0,对象依然存在,不会被垃圾回收。 而另外一个对象调用这个class 的时候还是会创建一个对象,然后bean的管理依然是由调用者来主导了,这样的话就会造成了很多的耦合,还有对象的过多的的创建而又没有被回收,浪费了内存资源。而自从有了IOC,那么bean的创建就交给IOC容器了。这样就将bean的整个生命周期交由IOC容器处理。 那么什么是DI, 依赖注入(Dependency Injection,简称DI),所谓的DI 就是为了管理bean 与bean 之间的依赖关系的。实际应用中,我们很多场景下,bean与之间是相互依赖的, 那么Spring就提供了,DI这样的一种方式来实现bean之间的依赖。
至于IOC 与DI的关系,我看多好多的版本,有人说DI 是IOC的一部分。有人说是IOC ,DI 是两个不同的概念, 个人认为, DI 是IOC的一部分,因为DI的原理就是为了使得bean的创建过程中支持了同时加载依赖bean。 不管是啥关系,我们首先把两个概念搞清楚就可以。
二、 Spring IOC体系结构
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
(1) BeanFactory
在IOC的实现源码的最高层级的bean是 BeanFactory, BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范,
具体层级关系如图:
1. listablebeanfactory
让我们先看看,最左边的一个继承的接口listablebeanfactory,
官方的解释是:
Extension of the {@link BeanFactory} interface to be implemented by bean factories
that can enumerate all their bean instances, rather than attempting bean lookup
by name one by one as requested by clients. BeanFactory implementations that
preload all their bean definitions (such as XML-based factories) may implement
this interface.
扩展BeanFactory接口,提供所有bean 实例的枚举,不再需要客户端通过一个个bean name查找.BeanFactory实现类预加载bean定义(如通过实现xml的工厂)需要实现这个接口.
2. HierarchcalBeanFactory 从源码中我们可以看到HierarchcalBeanFactory较之BeanFactory多了两个方法getParentBeanFactory可以查看双亲的BeanFactory,还有个就是containsLocalBean该工厂是否包含一个bean。
3 . AutowireCapableBeanFactory在BeanFactory基础上实现了对存在实例的管理.可以使用这个接口集成其它框架,捆绑并填充并不由Spring管理生命周期并已存在的实例.像集成WebWork的Actions 和Tapestry Page就很实用.
一般应用开发者不会使用这个接口,所以像ApplicationContext这样的外观实现类不会实现这个接口,如果真手痒痒可以通过ApplicationContext的getAutowireCapableBeanFactory接口获取.
AutowireCapableBeanFactory源码 具体:
1、总共5个静态不可变常量来指明装配策略,其中一个常量被Spring3.0废弃、一个常量表示没有自动装配,另外3个常量指明不同的装配策略——根据名称、根据类型、根据构造方法。
2、8个跟自动装配有关的方法,实在是繁杂,具体的意义我们研究类的时候再分辨吧。
3、2个执行BeanPostProcessors的方法。
4、2个分解指定依赖的方法
总结:这个工厂接口继承自BeanFacotory,它扩展了自动装配的功能,根据类定义BeanDefinition装配Bean、执行前、后处理器等。
4 . ConfigurableBeanFactory:
定义BeanFactory的配置.ConfigurableBeanFactory中定义了太多太多的api,比如类加载器,类型转化,属性编辑器,BeanPostProcessor,作用域,bean定义,处理bean依赖关系,合并其他ConfigurableBeanFactory,bean如何销毁.
ConfigurableBeanFactory同时继承了HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,即同时继承了分层和单例类注册的功能。
具体:
1、2个静态不可变常量分别代表单例类和原型类。
2、1个设置父工厂的方法,跟HierarchicalBeanFactory接口的getParentBeanFactory方法互补。
3、4个跟类加载器有关的方法:get/set工厂类加载器和get/set临时类加载器。
4、2个设置、是否缓存元数据的方法(热加载开关)。
5、11个处理Bean注册、加载等细节的方法,包括:Bean表达式分解器、转换服务、属性编辑登记员、属性编辑器、属性编辑注册器、类型转换器、嵌入式的字符串分解器
6、2个处理Bean后处理器的方法。
7、3个跟注册范围相关的方法。
8、1个返回安全访问上下文的方法、1个从其他的工厂复制相关的所有配置的方法。
9、2个跟Bean别名相关的方法、1个返回合并后的Bean定义的方法。
10、1个判断是否为工厂Bean的方法、2个跟当前Bean创建时机相关的方法。
11、3个跟Bean依赖相关的方法、3个销毁Bean相关的方法。
总结:这个巨大的工厂接口,继承自HierarchicalBeanFactory 和 SingletonBeanRegistry 这两个接口,并额外独有37个方法!!!(看的我都快疯了…)这37个方法包含了工厂创建、注册一个Bean的众多细节。这个工厂名为ConfigurableBeanFactory,真是名不虚传!统计一下此时的ConfigurableBeanFactory的方法数吧。自有的37个方法、HierarchicalBeanFactory的2个方法、SingletonBeanRegistry的5个方法、爷爷接口BeanFactory的10个方法,共有54个方法!虽然方法繁多,还算井井有条!
5. ConfigurableListableBeanFactory
ConfigurableListableBeanFactory具体:
1、2个忽略自动装配的的方法。
2、1个注册一个可分解依赖的方法。
3、1个判断指定的Bean是否有资格作为自动装配的候选者的方法。
4、1个根据指定bean名,返回注册的Bean定义的方法。
5、2个冻结所有的Bean配置相关的方法。
6、1个使所有的非延迟加载的单例类都实例化的方法。
总结:工厂接口ConfigurableListableBeanFactory同时继承了3个接口,ListableBeanFactory、AutowireCapableBeanFactory 和 ConfigurableBeanFactory,扩展之后,加上自有的这8个方法,这个工厂接口总共有83个方法,实在是巨大到不行了。这个工厂接口的自有方法总体上只是对父类接口功能的补充,包含了BeanFactory体系目前的所有方法,可以说是接口的集大成者。
6 .AbstractAutowireCapableBeanFactory这个类的功能就是加载配置文件,然后实例化bean,并维护它们。从它的构造函数可以看出,其在创建bean的时候会忽略掉依赖的bean的创建。也就是说,当A中有属性B,那么当Spring在获取A的Bean的时候,如果其属性B还没有初始化,且B没有实现BeanNameAware,BeanFactoryAware,BeanClassLoaderAware接口,那么spring会自动初始化为B。如果B实现了这些接口,是不会自动初始化B的。
说了那么多的接口, 终于最后一个非接口的bean DefaultListableBeanFactory : 不得不说是集大成者,是bean加载的核心。
默认实现了ListableBeanFactory和BeanDefinitionRegistry接口,基于bean definition对象,是一个成熟的bean factroy。
最典型的应用是:在访问bean前,先注册所有的definition(可能从bean definition配置文件中)。使用预先建立的bean定义元数据对象,从本地的bean definition表中查询bean definition因而将不会花费太多成本。
DefaultListableBeanFactory既可以作为一个单独的beanFactory,也可以作为自定义beanFactory的父类。
注意:特定格式bean definition的解析器可以自己实现,也可以使用原有的解析器,如:
PropertiesBeanDefinitionReader和XmLBeanDefinitionReader。
(2) BeanDefinition
下面我们来看看: BeanDefinition
首先就是BeanDefinition的类定义:
对,没错,这货是个接口,而不是类,是不是有点莫名奇妙呢?我们都知道在JAVA中,接口是不能用来new出新的对象的,那么在Spring中,到底将XML解析出来的Bean包装成了什么呢?(这个密等下揭开)
先来看看BeanDefinition一个继承结构吧(均是与BeanDefinition有直接关联的类或者接口)!
从类图中可以看出,BeanDefinition继承了AttributeAccessor和BeanMetadataElement两个接口;一个一个看。
AttributeAccessor
AttributeAccessor接口定义了最基本的对任意对象的元数据的修改或者获取,主要方法有:
BeanMetadataElement
BeanMetadataElement接口提供了一个getResource()方法,用来传输一个可配置的源对象。
一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor 例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据(译注)。
其主要的属性和方法有:
说白了,就是我们在配置文件中的bean 标签定义的属性 在这个bean中都有定义。
三、 IoC容器的初始化
我们既然知道了,工厂类可以产生bean,那么一个工厂类怎么去创建bean的呢。
1、 XmlBeanFactory(屌丝IOC)的整个流程
XmlBeanFactory继承自DefaultListableBeanFactory,扩展了从xml文档中读取bean definition的能力。从本质上讲,XmlBeanFactory等同于DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考虑使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因为该方案可以从多个xml文件读取资源,并且在解析xml上具有更灵活的可配置性。
源码解读:
通过前面的源码,this.reader = new XmlBeanDefinitionReader(this); 中其中this 传的是factory对象。
1 XmlBeanFactory的使用
典型构造方法:
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
1
2
3
4
reader是XmlBeanDefinitionReader的实例,XmlBeanDefinitionReader继承自AbstractBeanDefinitionReader(http://www.cnblogs.com/davidwang456/p/4190428.html 已经介绍过)。
Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。这样所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者。另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由 ResourceLoader 接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,他的默认实现是 DefaultResourceLoader。
今天先介绍到这里,接下来,下次继续。
以上是关于Spring IOC设计原理解析的主要内容,如果未能解决你的问题,请参考以下文章