IOC和DI
IOC(Inversion Of Control):控制反转,是Spring的核心。所谓控制反转,就是将对象的创建和管理交由Spring容器控制,这是一种重要的面向对象设计思想,能够帮助我们设计出低耦合的程序。
DI(Dependency Injection):依赖注入。在运行期间,动态地为对象注入其所依赖的对象,提高组件的复用,提高程序的灵活性和可扩展性。
控制反转和依赖注入是对同一概念的不同描述,控制反转以容器的角度描述,而依赖注入则是以应用程序的角度描述,这两种说法殊途同归,分离了对象和其所依赖的资源,使得程序更加灵活,为我们提供了重要的面向对象设计思想。
Spring的IOC容器设计
Spring中有两个系列的容器:
实现BeanFactory接口的简单IOC容器,这些容器提供了IOC容器的基本功能。
实现ApplicationContext接口的应用上下文,这些应用上下文不仅提供了IOC容器的基本功能,而且提供了一些高级功能,是IOC容器的高级形式。
IOC容器接口设计图如下图所示,Spring设计了如此之多的IOC容器接口,一方面可以通过接口的叠加来扩展IOC容器的功能,另一方面可以方便特定容器的定制实现。
BeanFactory系列容器接口
从上图可以看到,BeanFactory是IOC容器接口中最顶层的接口,定义了IOC容器的基本规范。BeanFactory共有三个直接的子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。
ListableBeanFactory接口表示Bean是可列表化的。
HierarchicalBeanFactory接口表示Bean是有继承关系的,可以管理父IOC容器。
AutowireCapableBeanFactory接口表示Bean是自动装配的。
从上图中可以看到,BeanFactory系列接口中一个主要的继承路径就是从BeanFactory到ConfigurableListableBeanFactory。在HierarchicalBeanFactory接口的基础上,扩展了对BeanFactory的配置功能,如设置父IOC容器、配置Bean后置处理器等。
BeanFactory接口定义了IOC容器最基本的形式,这些功能的定义可以在BeanFactory接口中看到,通过这一系列的接口方法,可以实现不同的IOC容器,接口代码如下所示:
1 | public interface BeanFactory { |
BeanFactory接口只定义了IOC容器的基本行为,忽略了IOC容器的具体实现,要知道工厂是如何生产和管理对象的,就需要深入到具体IOC容器的实现,DefaultListableBeanFactory、XmlBeanFactory等就是基本IOC容器的具体实现,下面以XmlBeanFactory为例看看IOC容器的简单实现。
XmlBeanFactory是一个简单的IOC容器的实现,它继承自DefaultListableBeanFactory。Spring中将DefaultListableBeanFactory作为一个默认的功能完整的IOC容器使用,XmlBeanFactory在DefaultListableBeanFactory的基础上扩展了读取以XML格式定义的BeanDefinition(Spring中描述Bean的类)的功能,XmlBeanFactory源码如下所示:
1 |
|
下面尝试使用XmlBeanFactory,代码如下:
1 | ClassPathResource resource = new ClassPathResource("beans.xml"); |
可见XmlBeanFactory的使用是非常简单的,但这个类已经在Spring3.1中被标明废弃。XmlBeanFactory继承自DefaultListableBeanFactory,是对后者的功能扩展,我们可以通过编程直接使用后者来达到前者的功能,代码如下:
1 | ClassPathResource resource = new ClassPathResource("beans.xml"); |
以上代码使用了DefaultListableBeanFactory这一功能功能完整的IOC容器,主要分为以下四个步骤:
创建IOC容器配置文件的资源,这里使用ClassPathResource,即在ClassPath中寻找资源文件。
创建一个BeanFactory,这里使用了DefaultListableBeanFactory。
创建一个BeanDefinition读取器,这里使用XmlBeanDefinitionReader,用于加载以XML文件格式定义的BeanDefinition,并回调给BeanFactory。
从资源中读取Bean的配置信息,并完成Bean的加载和注册。
ApplicationContext系列容器接口
ApplicationContext作为一系列重要的容器产品,一方面继承了BeanFactory基本的IOC容器功能;另一方面,对BeanFactory进行了功能扩展。
如下图所示,ApplicationContext通过继承ApplicationEventPublisher、ResourcePatternResolver、MessageSource、EnvironmentCapable接口,为基本的IOC容器扩展了高级的容器特性,例如:
- ApplicationEventPublisher:支持应用事件
- MessageSource:支持国际化信息源
- ResourcePatternResolver: 允许以路径模式定位Resource
- EnvironmentCapable:应用上下文环境检查
FileSystemXmlApplicationContext和ClassPathXmlApplicationContext是常用的应用上下文,根据名字可以看出二者分别实现了从文件系统和类路径读取以XML格式定义的BeanDefinition的功能,下面以FileSystemXmlApplicationContext为例看一看应用上下文的设计原理。
FileSystemXmlApplicationContext的类继承关系如下图所示。
关于应用上下文的主要功能已经在其父类AbstractXmlApplicationContext中实现了,而FileSystemXmlApplicationContext主要进行了两方面的功能扩展:
1.构造应用上下文实例,同时启动IOC容器的refresh()过程,代码如下
1 | /** |
refresh()中具体的处理过程已经定义在AbstractApplicationContext中,在子类应用上下文中只需要显式调用即可。refresh()中进行了刷新准备、创建BeanFactory、注册消息源等一系列操作。
2.定义从文件路径加载XML资源文件的方法,代码如下:
1 | /** |
FileSystemXmlApplicationContext重写了DefaultResourceLoader中的getResourceByPath(String path)方法,提供了通过文件系统路径定位资源文件的功能。在DefaultResourceLoader中,默认实现是通过ClassPath加载资源文件的,代码如下所示:
1 | protected Resource getResourceByPath(String path) { |
参考资料:《Spring技术内幕》
最后的最后,安利一下自己写的一个Java代码生成工具,能够方便的生成Spring、SpringMVC、Mybatis架构下的Java代码,希望能对大家有所帮助,地址:Java代码生成器:Generator