Spring揭秘-Spring的IoC容器之BeanFactory

本章内容

  • 拥有BeanFactory之后的生活
  • BeanFactory的对象注册与依赖绑定方式
  • BeanFactory的XML之旅
  • 容器背后的秘密

Spring的IoC容器是一个IoC Service Provider,但是,这只是它被冠以IoC之名的部分原因,我们不能忽略的是“容器”。 Spring的IoC容器是一个提供IoC支持的轻量级容器,除了基本的IoC支持,它作为轻量级容器还提供了IoC之外的支持。如在Spring的IoC容器之上, Spring还提供了相应的AOP框架支持、企业级服务集成等服务。 Spring的IoC容器和IoC Service Provider所提供的服务之间存在一定的交集,二者的关系如图:

Spring提供了两种容器类型: BeanFactory和ApplicationContext。
BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略( lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对
该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需
要的资源有限。对于资源有限,并且功能要求不是很严格的场景, BeanFactory是比较合适的
IoC容器选择。

ApplicationContext。 ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持, ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,这些会在后面详述。 ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说, ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。

如图,我们可以对BeanFactory和ApplicationContext之间的关系有一个更清晰的认识。

注意
ApplicationContext间接继承自BeanFactory,所以说它是构建于BeanFactory之上的IoC容器。此外,你应该注意到了, ApplicationContext还继承了其他三个接口,它们之间的关系,我们将在后续文章中详细说明。

另外,在没有特殊指明的情况下,以BeanFactory为中心所讲述的内容同样适用于ApplicationContext,这一点需要明确一下,二者有差别的地方会在合适的位置给出解释。

BeanFactory,顾名思义,就是生产Bean的工厂。当然,严格来说,这个“生产过程”可能不像说起来那么简单。既然Spring框架提倡使用POJO,那么把每个业务对象看作一个JavaBean对象,或许更容易理解为什么Spring的IoC基本容器会起这么一个名字。作为Spring提供的基本的IoC容器,BeanFactory可以完成作为IoC Service Provider的所有职责,包括业务对象的注册和对象间依赖关系的绑定。

BeanFactory就像一个汽车生产厂。你从其他汽车零件厂商或者自己的零件生产部门取得汽车零件送入这个汽车生产厂,最后,只需要从生产线的终点取得成品汽车就可以了。相似地,将应用所需的所有业务对象交给BeanFactory之后,剩下要做的,就是直接从BeanFactory取得最终组装完成并且可用的对象。至于这个最终业务对象如何组装,你不需要关心, BeanFactory会帮你搞定。

所以,对于客户端来说,与BeanFactory打交道其实很简单。最基本地, BeanFactory肯定会公开一个取得组装完成的对象的方法接口,如BeanFactory的定义所展示的那样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String name) throws BeansException;
Object getBean(String name, Class requiredType) throws BeansException;
/**
* @since 2.5
*/
Object getBean(String name, Object[] args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* @since 2.0.3
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* @since 2.0.1
*/
boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
Class getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
JAVA

上面代码中的方法基本上都是查询相关的方法,例如,取得某个对象的方法( getBean)、查询某个对象是否存在于容器中的方法( containsBean),或者取得某个bean的状态或者类型的方法等。因为通常情况下,对于独立的应用程序,只有主入口类才会跟容器的API直接耦合。

拥有BeanFactory之后的生活

确切地说,拥有BeanFactory之后的生活没有太大的变化。当然,意思是看起来没有太大的变化,那么到底引入BeanFactory后的生活是什么样子,让我们一起来体验一下吧!

依然“拉拉扯扯的事情” 。对于应用程序的开发来说,不管是否引入BeanFactory之类的轻量级容器,应用的设计和开发流程实际上没有太大改变。换句话说,针对系统和业务逻辑,该如何设计和
实现当前系统不受是否引入轻量级容器的影响。对于例子中的FX新闻系统,我们还是会针对系统需求,分别设计相应的接口和实现类。前后唯一的不同,就是对象之间依赖关系的解决方式改变了。这就是所谓的“拉拉扯扯的事情”。之前我们的系统业务对象需要自己去“拉”( Pull)所依赖的业务对象,有了BeanFactory之类的IoC容器之后,需要依赖什么让BeanFactory为我们推过来( Push)就行了。

所以,简单点儿说,拥有BeanFactory之后,要使用IoC模式进行系统业务对象的开发。以下代码演示了FX新闻系统初期的设计和实现框架代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1-设计FXNewsProvider类用于普遍的新闻处理
public class FXNewsProvider
{
...
}
2-设计IFXNewsListener接口抽象各个新闻社不同的新闻获取方式,并给出相应实现类
public interface IFXNewsListener
{
...
}
以及
public class DowJonesNewsListener implements IFXNewsListener
{
...
}
3-设计IFXNewsPersister接口抽象不同数据访问方式,并实现相应的实现类
public interface IFXNewsPersister
{
...
}
以及
public class DowJonesNewsPersister implements IFXNewsPersister
{
...
}
JAVA

BeanFactory会说,这些让我来干吧。既然使用IoC模式开发的业务对象现在不用自己操心如何解决相互之间的依赖关系,那么肯定得找人来做这个工作。毕竟,工作最终是要有人来做的,大家都不动手,那工作就不能进行了。当BeanFactory说这些事情让它来做的时候,可能没有告诉你它会怎么来做这个事情。不过没关系,稍后会详细分析它是如何做的。通常情况下,它会通过常用的XML文件来注册并管理各个业务对象之间的依赖关系。

1
2
3
4
5
6
7
8
9
10
11
<beans>
<bean id="djNewsProvider" class="..FXNewsProvider"> 8
<constructor-arg index="0">
<ref bean="djNewsListener"/>
</constructor-arg>
<constructor-arg index="1"> 9
<ref bean="djNewsPersister"/>
</constructor-arg>
</bean> 10
...
</beans>
XML

拉响启航的汽笛。在BeanFactory出现之前,我们通常会直接在应用程序的入口类的main方法中,自己实例化相应的对象并调用之,如以下代码所示:

1
2
FXNewsProvider newsProvider = new FXNewsProvider();
newsProvider.getAndPersistNews();
JAVA

不过,现在既然有了BeanFactory,我们通常只需将“生产线图纸”交给BeanFactory,让BeanFactory为我们生产一个FXNewsProvider,如以下代码所示:

1
2
3
4
BeanFactory container =  
new XmlBeanFactory(new ClassPathResource("配置文件路径"));
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
JAVA

或者如以下代码所示:

1
2
3
4
ApplicationContext container = 
new ClassPathXmlApplicationContext("配置文件路径");
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
JAVA

亦或如以下代码所示:

1
2
3
4
ApplicationContext container =   
new FileSystemXmlApplicationContext("配置文件路径");
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider");
newsProvider.getAndPersistNews();
JAVA

Spring揭秘-Spring的IoC容器之BeanFactory
https://leehoward.cn/2019/10/24/Spring揭秘-Spring的IoC容器之BeanFactory/
作者
lihao
发布于
2019年10月24日
许可协议