Spring的IoC容器之BeanFactory
Spring IoC 容器由两个部分组成:IoC和容器;所谓IoC,即IoC Service Provider,也就是说它能够完成掌握大局的IoC Service Provider这篇文章里提到的相关工作;除了基本的IoC支持,作为轻量级容器,它还提供了相应AOP框架、企业级服务集成等支持。如下图:
Spring 提供了两种容器类型:BeanFactory和ApplicationContext。
- BeanFactory:基础类型IoC容器,提供完整的IoC服务,默认采用Lazy-Load初始化策略;只有当客户端需要访问某个容器管理的对象时才会对该对象进行初始化以及依赖注入;所以前期容器启动速度较快,所需要的资源少,但是运行时速度就较低;适合在系统资源有限、功能要求不太严格的场景下使用;
- ApplicationContext:构建于BeanFactory之上,是较高级的IoC容器;除了拥有BeanFactory的全部功能外,其还提供了其他特性,如事件发布、国际化信息等。默认在容器启动时初始化所有对象并完成依赖绑定;需要更多的系统资源,启动速度较慢,但是运行时速度较好;适合在系统资源充足、功能要求较高的场景下使用;
它们之间的关系如下:
Spring提倡基于POJO来构造业务系统的,每一个业务对象就是一个JavaBean,而负责创建这些JavaBean的类,自然就是一个Factory了,于是就有了BeanFactory。
作为Spring提供的基本的IoC容器,BeanFactory可以完成作为IoC Service Provider的所有职责,包括业务对象的注册和对象间依赖关系的绑定;
将应用所需的所有业务对象交给BeanFactory之后,剩下要做的,就是直接从BeanFactory取得最终组装完成并且可用的对象;至于如何组装,交给BeanFactory就好;
接下来,我们就走进BeanFactory一探究竟~
拥有BeanFactory之后
确切地说,拥有BeanFactory之后的生活没有太大的变化。
对于应用程序的开发来说,不管是否引入BeanFactory之类的轻量级容器,应用的设计和开发流程实际上没有太大改变。换句话说,针对系统和业务逻辑,该如何设计和实现,不受是否引入轻量级容器的影响。
前后唯一的不同,就是对象之间依赖关系的解决方式改变了。
所以,简单点儿说,拥有BeanFactory之后,要使用IoC模式进行系统业务对象的开发。实际上,即使不使用BeanFactory之类的轻量级容器支持开发,开发中也应该尽量使用IoC模式。还记得面向对象程序设计的六大理论吗?(不记得没关系,移步看看就好!),控制反转可是六大理论之一哦!
既然使用IoC模式开发的业务对象现在不用自己操心如何解决相互之间的依赖关系,那么肯定得找人来做这个工作。**毕竟,工作最终是要有人来做的,大家都不动手,那工作就不能进行了。**这个人当然就是BeanFactory啦,它会去做,你只要相信它能完成工作即可,至于它是怎样完成的。。。额,我们会知道的!
掌握大局的IoC Service Provider这篇文章里,我们提到IoC Service Provider的三种工作方式,当然也适用于Spring提供的IoC Service Provider了。一般来说,会使用配置文件中的xml来描述对象间的依赖关系。
但是说实话,xml也快退出历史舞台了,因为可以通过注解来标记容器需要管理的对象。当容器知道它所管理的对象后,也就知道如何为这些对象服务,即完成依赖绑定了。相对于xml等配置,注解无疑降低了工作量;
好了,我们来具体看看Spring IoC 容器是怎样工作的吧!
BeanFactory的对象注册与依赖注入
BeanFactory作为一个IoC Service Provider,为了能够明确管理各个业务对象以及业务对象之间的依赖绑定关系,同样需要某种途径来记录和管理这些信息;
直接编码
上一篇文章中也提到:通过程序编码让最终的IoC Service Provider(也就是各个IoC框架或者容器实现)得以知晓服务的“奥义”,应该是管理依赖绑定关系的最基本方式。不管哪种方式,最后都需要编码才能落实所有信息并付诸使用;
在学习相关代码之前,我们还需要掌握一些术语。在Spring的术语中,把BeanFactory需要使用的对象注册和依赖绑定信息称为Configuration Metadata。所以,我们这里所展示的,实际上就是组织这些Configuration Metadata的各种方式。当然,在这里就是通过代码来组织这些Configuration Metadata了。
public class Main {
public static void main(String[] args){
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
bindViaCode(beanFactory);
Boss boss=(Boss)beanFactory.getBean("boss");
boss.doProject();
}
private static void bindViaCode(BeanDefinitionRegistry registry) {
AbstractBeanDefinition workerDefinition=new RootBeanDefinition(Worker.class);
AbstractBeanDefinition projectDefinition=new RootBeanDefinition(Project.class);
AbstractBeanDefinition bossDefinition=new RootBeanDefinition(Boss.class);
registry.registerBeanDefinition("worker",workerDefinition);
registry.registerBeanDefinition("project",projectDefinition);
registry.registerBeanDefinition("boss",bossDefinition);
//通过构造方法注入Boss的依赖
ConstructorArgumentValues argumentValues=new ConstructorArgumentValues();
argumentValues.addIndexedArgumentValue(0,workerDefinition);
argumentValues.addIndexedArgumentValue(1,projectDefinition);
bossDefinition.setConstructorArgumentValues(argumentValues);
//通过setter方法注入Boss依赖
// MutablePropertyValues propertyValues=new MutablePropertyValues();
// propertyValues.addPropertyValue(new PropertyValue("worker",workerDefinition));
// propertyValues.addPropertyValue(new PropertyValue("project",projectDefinition));
// bossDefinition.setPropertyValues(propertyValues);
//通过setter方法注入project相关信息
MutablePropertyValues propertyValues=new MutablePropertyValues();
propertyValues.addPropertyValue(new PropertyValue("name","Spring IoC"));
propertyValues.addPropertyValue(new PropertyValue("time","2个月"));
projectDefinition.setPropertyValues(propertyValues);
}
}
public class Boss {
private Worker worker;
private Project project;
public Boss(Worker worker, Project project) {
this.worker = worker;
this.project = project;
}
public void doProject(){
worker.doProject(project);
}
}
public class Project {
private String name="Spring";
private String time="一个月";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
public class Worker {
private String name="Nil Xuan";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void doProject(Project project){
System.out.println(name+"正在进行为期"+project.getTime()+"的"+project.getName()+"项目");
}
}
上面我们看到的PropertyValue、BeanDefinition、以及ValueHolder(ConstructorArgumentValues的内部类)都是Spring为收集与Bean定义相关信息而提供的类,它们的共同父类是BeanMetadataElement。因为这里只介绍用法,不涉及源码探究,所以就不再深入;
BeanFactory只是一个接口,我们最终需要一个该接口的实现来进行实际的Bean的管理, DefaultListableBeanFactory就是这么一个比较通用的BeanFactory实现类。DefaultListableBeanFactory除了间接地实现了BeanFactory接口,还实现了BeanDefinitionRegistry接口,该接口才是在BeanFactory的实现中担当Bean注册管理的角色。
基本上,BeanFactory只是定义了访问容器所管理的Bean的方法,各个实现类将负责具体的注册和管理工作;
每一个受管的对象,在容器中都会有一个BeanDefinition的实例( instance)与之相对应,该BeanDefinition的实例负责保存对象的所有必要信息,包括其对应的对象的class类型、是否是抽象类、构造方法参数以及其他属性等。当客户端向BeanFactory请求相应对象的时候, BeanFactory会通过这些信息为客户端返回一个完备可用的对象实例。
外部配置文件方式
Spring的IoC容器支持两种配置文件格式: Properties文件格式和XML文件格式。如果你愿意也可以引入自己的文件格式。
Spring根据不同的外部配置文件格式,会给出相应的BeanDefinitionReader实现类,由BeanDefinitionReader的相应实现类负责将相应的配置文件内容读取并映射到BeanDefinition,然后将映射后的BeanDefinition注册到一个BeanDefinitionRegistry,之后, BeanDefinitionRegistry即完成Bean的注册和加载。
大部分工作,包括解析文件格式、装配BeanDefinition之类的工作,都是由BeanDefinitionReader的相应实现类来做的, BeanDefinitionRegistry只不过负责保管而已。
可以看出,基本上在Spring对通过外部配置文件来描述的依赖关系的处理流程为:BeanDefinitionReader->BeanDefinition->BeanDefinitionRegistry。我们需要做的就是按照配置文件所要求的语法来描述对象之间的依赖关系;
因为通过Properties文件来描述对象依赖关系的做法基本上已被淘汰,所以这里也不做了解。但是xml描述方式作为一种Spring经典的依赖描述手段,对于我们更好滴理解Spring IoC的概念和其他描述方式都有着不小的帮助,所以我们仍将详细学习基于xml的spring依赖描述规则;
因为xml本身具有很强的描述性,所以基于xml的配置文件也就比较好理解,但是对于每一种节点其拥有哪些属性、可以包含哪些子元素,仍需要注意。我们会专门总结基于xml的依赖描述,这里只为提供一个整体的认识,不做详细介绍;
不管是Properties文件还是XML配置文件,Spring的处理流程是一致的,我们根据该流程便可以方便地扩展属于我们自己的依赖描述方式,这就是Spring的可扩展性;
注解方式
注解是一种类级别的描述依赖关系的方式;它使用类似@Component、@Autowired等注解告诉Spring,其所在的类需要被Spring IoC容器管理;
我们可以通过注解告诉Spring它需要服务的对象,但是我们同样需要告诉Spring,它需要按照这样的方式来工作;于是,我们还是需要在xml里做出配置,来告诉Spring:就按注解的方式为我们提供服务吧!
这一配置项就是:<context:component-scan base-package="…">;Spring就会去package下寻找那些拥有注解的类啦;