这是Spring基础知识系列的最后一个章节,让我们回顾一下Spring核心功能:Ioc容器。我们初始化一个Spring容器时,是创建一个ApplicationContext的实现类,需要直接访问容器功能时,大多也是调用ApplicationContext方法;仿佛容器这个抽象概念的具体形式就是ApplicationContext。
这样的理解本身也没有大问题,不过ApplicationContext不仅仅是一个bean容器,它还提供了很多其他的功能模块,比如Event机制。为了解耦,也为了降低ApplicationContext自身的复杂度,Spring将核心的容器功能(bean的创建和缓存)抽象为另一个接口:BeanFactory。
BeanFactory
BeanFactory,顾名思义,是一个bean的工厂接口,负责bean的初始化,并向外提供查询bean的方法。ApplicationContext扩展了BeanFactory,但ApplicationContext的具体实现,比如GenericApplicationContext,没有直接实现BeanFactory的功能,而是在内部包含一个DefaultListableBeanFactory对象,并将相关方法调用转发给后者。
这个设计本质上是把容器最核心的功能,和其它功能模块隔离开。DefaultListableBeanFactory不关注bean的定义是来自xml还是来自Annotation,也不会对BeanPostProcessor这样的bean另眼相看,它的核心功能就是接受bean定义元数据,并按要求完成bean的创建、初始化、依赖注入等。
下面的表格简要罗列了一下BeanFactory和ApplicationContext的功能对比:
功能 | BeanFactory | ApplicationContext |
---|---|---|
Bean初始化和依赖注入 | yes | yes |
Bean的生命周期管理 | no | yes |
BeanPostProcessor发现和激活 | no | yes |
BeanFactoryPostProcessor的发现和激活 | no | yes |
MessageSource(国际化) | no | yes |
事件机制 | no | yes |
再看一段直接使用DefaultListableBeanFactory的代码:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//读入bean配置
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
//读入属性问你件
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
//替换属性占位符
cfg.postProcessBeanFactory(factory);
这个代码创建了DefaultListableBeanFactory,通过XmlBeanDefinitionReader从xml文件读取bean定义,然后手动创建了一个PropertySourcesPlaceholderConfigurer(一个BeanFactoryPostProcessor),读入属性定义文件,手动调用它postProcessBeanFactory方法来处理factory内所有的BeanDefinition。
上面的代码很繁琐,我们不会在项目中直接使用,但是可以让我们对更加直观的理解ApplicationContext和BeanFactory之间的关系。
总结
容器功能是Spring的基石,Spring框架的上层功能模块往往是通过向容器预置一些特定功能bean来集成的;而用户代码要扩充框架功能,也是通过向容器注册某些实现特定接口的bean。这可以说是Spring框架的设计哲学,理解这一点,对正确使用Sping,学习Spring大家族的其他技术,至关重要。