摘要
IoC(Inversion of Control,控制反转)原则,又可以称为DI(Dependency Injection,依赖注入),Bean的整个生命周期交由IoC容器控制,依赖注入能够使代码更加整洁及解耦。这里选取了IoC容器的Bean依赖注入、Bean的作用域和Bean的生命周期这几个方向进行讨论。
一、什么是IoC
IoC(Inversion of Control,控制反转)原则,又可以称为DI(Dependency Injection,依赖注入)。
在IoC设计原则中,由对象定义其依赖关系,对象实例由IoC容器(工厂模式)创建,并注入其依赖项。Bean的整个生命周期交由IoC容器控制,依赖注入能够使代码更加整洁及解耦。
BeanFactory和ApplicationContext
- BeanFactory是IoC容器的核心接口,其提供了IoC的最基础功能
- ApplicaitonContext是BeanFactory的子接口,其在BeanFactory接口的基础上增加了一些特性,包括:整合AOP、信息源(国际化)、事件发布、为应用提供应用级别的上下文信息
通常情况下应该使用ApplicationContext接口。
二、IoC容器概述
ApplicationContext接口体现了IoC容器和接口实例化、配置、组装Bean的能力。IoC容器通过读取配置的元数据信息来实现上述的功能的,这些元数据信息可以来源于XML、Java注解、Java代码。
如上图所示,你向IoC容器输入Bean对象(POJO)和元数据配置(描述依赖关系),IoC就可以为应用提供Bean实例使用了。
Bean的元数据信息配置
Bean的元数据信息配置,描述了Bean的属性及其依赖关系,来源于以下几点:
- XML
- 注解(Spring 2.5 开始支持)
- Java代码(Spring 3.0 开始支持)
在Spring中使用BeanDefinition对象表示,具体的元数据配置信息如下:
- 类名,通常是Bean的实际实现类
- Bean的行为配置信息,用来表示Bean在容器内的行为,如:作用域、生命周期回调等
- 当前Bean对其它Bean的依赖信息
- 创建对象时的其它配置信息,如连接池中的连接数量
以上的信息构建成不同BeanDefinition对象。
以下为使用XML来定义Bean的信息:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
IoC容器的实例化
IoC容器实例化的三个基本过程:
- Resource定位:Bean的定义文件定位
- 载入:将Resource定位好的资源载入到BeanDefinition
- 注册:将BeanDefinition注册到容器中
使用IoC容器
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
三、IoC容器的一些讨论
这里选取了IoC容器的Bean依赖注入、Bean的作用域和Bean的生命周期这几个方向进行讨论。
1、Bean依赖注入
应用中,Bean不可能只有单独的一个Bean,现实中肯定会存在不同Bean之间的协作依赖。下面讨论的是IoC容器如何处理Bean之间的依赖问题。
依赖注入存在两种方式,分别是构造器注入和Setter注入。
IoC依赖注入过程如下:
- The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified by XML, Java code, or annotations.
- For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method (if you use that instead of a normal constructor). These dependencies are provided to the bean, when the bean is actually created.
- Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
- Each property or constructor argument that is a value is converted from its specified format to the actual type of that property or constructor argument. By default, Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, and so forth.
处理循环依赖问题
- Spring只能解决Setter方法注入的单例bean之间的循环依赖(注:即使用构造器注入,有可能会存在循环依赖的问题)
- ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在获取ClassA的实例时,不等ClassA完成创建就将其曝光加入正在创建的bean缓存中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。
2、Bean的作用域
Singleton
Bean的默认作用域,整个应用共享一份实例。
Prototype
该作用域每次请求Bean时都会新建一个Bean实例。
Request,Session,Application,WebSocket
The request, session, application, and websocket scopes are available only if you use a web-aware Spring ApplicationContext implementation (such as XmlWebApplicationContext).
这几个类型的作用域仅限在Web应用中使用。
- Request:每次Http请求对应一个Bean实例
- Session:每个Http会话对应一个Bean实例
- Application:每个ServletContext对应一个Bean实例
- WebSocket:每个WebSocket对应一个Bean实例
自定义作用域
可以通过实现org.springframework.beans.factory.config.Scope接口来自定义作用域并使用BeanFactory实现类来对新作用域进行注册。
3、Bean的生命周期
Bean的生命周期回调
Bean的生命周期线索:实例化、初始化、使用、销毁
可以利用Bean来实现Spring的InitializingBean、DisposableBean和BeanPostProcessor等可以对Bean的生命周期进行回调处理。