3.1 IOC容器
在第2章中介绍什么是依赖注入时有提到:依赖注入是组件之间的依赖关系由容器在运行期决定的,即由容器动态地将某个依赖关系注入组件之中。那什么是容器呢?既然Spring框架实现了IOC,那么Spring中的容器是什么呢?
3.1.1 容器介绍
在日常生活中,容器是指用以容纳物料并以壳体为主的基本装置,是用来盛放东西的。在编程中,容器是用来存储和组织其他对象的对象,首先要确定容器也是对象,也可以当作bean,只是这个对象是用来存储和组织其他对象的,那其他对象是什么呢?其他对象其实就是bean对象,这也是面向对象编程的一种体现,万物皆对象。Spring提供了BeanFactory、ApplicationContext两个IOC容器来管理众多的bean对象。
3.1.2 BeanFactory
一提到工厂,我们可能就会想到富某康。工厂是一类用以生产货物的大型工业建筑物。BeanFactory不是用来生产货物的,而是用来生产管理bean的。BeanFactory会在bean生命周期的各个阶段中对bean进行管理,并且Spring将这些阶段通过各种接口暴露给我们,让我们可以对bean进行各种处理。我们只要让bean实现对应的接口,那么Spring就会在bean的生命周期调用我们实现的接口来处理该bean。这是怎么实现的呢?主要分为以下两个阶段。
1.bean容器的启动
工厂要生产货物,首先得把工厂运转起来。同样,bean容器要管理bean,也需要先把容器启动起来,获取到bean的定义信息之后才能管理。
(1)读取bean的xml配置文件,然后将xml中每个bean元素分别转换成BeanDefinition对象。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { private volatile Object beanClass; private String scope = SCOPE_DEFAULT; private boolean abstractFlag = false; private boolean lazyInit = false; private int autowireMode = AUTOWIRE_NO; private int dependencyCheck = DEPENDENCY_CHECK_NONE; private String[] dependsOn; private ConstructorArgumentValues constructorArgumentValues; private MutablePropertyValues propertyValues; private String factoryBeanName; private String factoryMethodName; private String initMethodName; private String destroyMethodName;
BeanClass保存bean的class属性,scope保存Bean的作用域,abstractFlag保存该bean是否抽象,lazyInit保存是否延迟初始化,autowireMode保存是否自动装配,dependencyCheck保存是否坚持依赖,dependsOn保存该bean依赖于哪些bean(这些bean必须提取初始化),constructorArgumentValues保存通过构造函数注入的依赖,propertyValues保存通过setter方法注入的依赖,factoryBeanName和factoryMethodName用于factorybean,也就是工厂类型的bean,initMethodName和destroyMethodName分别对应bean的init-method和destroy-method属性。后面会对这些内容进行详细介绍。
(2)通过BeanDefinitionRegistry将bean注册到beanFactory中。
上面获取到bean的信息之后,是怎么注册到BeanFactory中的呢?其实是通过BeanDefinitionRegistry将bean注册到beanFactory中的,因为BeanFactory的实现类需要实现BeanDefinitionRegistry接口。
BeanDefinitionRegistry接口提供了根据beanName注册对应beanDefinition的方法。在DefaultListableBeanFactory类中实现了该方法,并将beanDefinition保存在了ConcurrentHash-Map中。
另外,Spring还对外暴露了一些接口来对bean初始化,例如BeanFactoryPostProcessor。
我们可以翻译一下postProcessBeanFactory的注释信息。postProcessBeanFactory可以修改应用上下文中已经进行standard初始化的beanFactory,此时所有bean的定义信息已经加载完成,但还未实例化,允许覆盖、新增甚至重新初始化bean信息,一个典型的例子就是属性覆盖器PropertyOverrideConfigurer。对于一些参数,可以配置在properties中,而不用配置在Spring的XML配置文件中。
2.bean容器的实例化
上面把bean容器启动之后,工厂算是运转起来了,配方(beanDefinition)也已经准备充分,然后就是生产(实例化)、管理货物(bean)了。实例化bean主要通过反射和CGLIB两种方式,在bean的实例化过程中,Spring也暴露了一些接口,如表3-1所示。
表3-1 在bean的实例化过程中Spring暴露的一些接口
我们可以通过示例演示一下这几个接口的使用。
(1)创建Maven project,在pom.xml中引入spring-core、spring-context。
(2)创建bean对象,实现上面列出的接口。
(3)bean配置。
(4)测试。
使用ApplicationContext获取BeanFactory,再通过getBean方法获取对应的bean,最后调用destroy方法进行销毁,从输出结果可以看到依次调用了BeanNameAware、BeanFactoryAware、InitializingBean、DisposableBean接口。
输出结果:
3.1.3 ApplicationContext
在上面的示例中,使用ApplicationContext获取了bean的配置,然后直接将ApplicationContext接口对象赋值给了BeanFactory接口对象,为什么可以赋值呢?其实ApplicationContext接口实现了BeanFactory接口。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver
从上面ApplicationContext接口的继承关系可以看到,它还通过继承其他接口扩展了BeanFactory的功能。
● MessageSource:为应用提供国际化访问功能。
● ResourcePattern Resolver:提供资源(如URL和文件系统)的访问支持。
● ApplicationEventPublisher:引入事件机制,包括启动事件、关闭事件等,让容器在上下文中提供了对应用事件的支持。它代表的是一个应用的上下文环境。
BeanFactory主要是面对Spring框架的基础设施,面对Spring自己。Applicationcontext主要面对于Spring使用的开发者。开发者基本都会使用Applicationcontext,而非BeanFactory,所以在上面实例中使用ApplicationContext获取BeanFactory接口对象。
上面的ApplicationContext对象是通过ClassPathXmlApplicationContext方法获取的,还有一种获取方式,即使用FileSystemXmlApplicationContext方法获取。
● ClassPathXmlApplicationContext表示从类路径下加载配置文件。文件路径默认指的是项目的classpath路径下,所以不需要写前缀“classpath:”,如果指向绝对路径,需要加上“file:”。
new ClassPathXmlApplicationContext(new String[] {"ApplicationContext.xml"});
● FileSystemXmlApplicationContext表示从文件系统中加载配置文件。文件路径默认指的是项目的根目录下,若想使用项目的classpath路径,则需要加上“classpath:”。
new FileSystemXmlApplicationContext(new String[]{"classpath: ApplicationContext.xml"});