对Spring IoC相关的知识做点笔记,涉及内容有Ioc概念、Spring Ioc容器、注入方式、容器启动方式以及Bean的生命周期。本篇不涉及源码的解读,只是介绍相关接口的功能和继承关系以及其实现类,大部分源于王福强的《Spring揭秘》一书,少部分源自其他博文。
IoC
控制反转Inversion of Control是一种思想,也叫依赖注入Dependency Injection,简单来说就是将所有的被注入对象和依赖对象交由IoC Service Provider来管理,控制对象也由被注入对象变为了IoC Service Provider。
一般依赖注入的方式:
- 构造器注入:即将被依赖对象作为被注入对象的构造器参数
- setter方法注入:为被依赖对象提供
setter
方法 - 接口注入:需要被注入对象实现一个接口,在接口提供的方法中将被依赖对象作为参数注入。(已经不再使用)
IoC Service Provider
IoC Service Provider的定义很宽泛,凡是能够实现将依赖对象和被注入对象进行绑定的角色都可以成为IoC Service Provider,它可以是一段代码、一组类、Ioc框架或者像Spring这样的IoC容器。它的职责主要由两个:
- 业务对象的构建管理
- 业务对象的依赖绑定
IoC Service Provider需要一些东西来记录对象之间的绑定关系,称为注册对象管理信息,这些信息可以是以下几种:
- 直接编码
- 配置文件
- 元数据:这部分稍微解释下,是指直接通过注解或其他方式标明依赖对象,而绑定关系则由其他类实现,这些类就成为被注入对象的元数据
BeanFactory
Spring的Ioc容器是一个提供IoC支持的轻量级容器,除了IoC外其还提供了诸如AOP等其他支持,本篇不进行展开,只关注其IoC部分。Spring提供了两种容器类型:
- BeanFactory
- ApplicationContext
ApplicationContext间接继承自BeanFactory,除了BeanFactory外其还继承了其他接口,实现了更高级的功能。两者最大的不同之处在于BeanFactory容器中管理的对象采用延迟初始化的策略,在被请求到时才会初始化一个对象实例,而ApplicationContext在容器启动之后会初始化所有的对象实例并完成绑定,这一特性导致在两个容器中Bean的生命周期有所不同,在下面介绍时会标注出来。
下面介绍BeanFactory相关功能的实现接口,ApplicationContext特有的功能接口将在之后列出。
主要顶层接口
BeanFactory
:定义了访问容器内管理的相关Bean实例的查询方法。BeanDefinition
:每个受管理的对象,容器内都有一个BeanDefinition的实例,负责保存对象的所有必要信息,包括class类型、是否是抽象类、构造方法及参数、其他属性。其主要有两个实现类,RootBeanDefinition
和ChildBeanDefinition
。BeanDefinitionRegistry
:与BeanDefinition为组合关系,抽象了Bean的注册管理逻辑。
BeanFactory的实现类都会同时实现BeanFactory
接口和BeanDefinitionRegistry
接口。
容器启动
容器启动阶段需要做下面几件事:
- 1.通过某种途径加载Configuration MetaData(可以理解为
Resource
) - 2.从Configuration MetaData中进行分析和解析,将解析到的信息映射到
BeanDefinition
上,需要依赖相关的工具类(BeanDefinitionReader
的实现类) - 3.将
BeanDefinition
注册到容器中
容器启动阶段有一个主要接口BeanFactoryPostProcessor
:它会在容器实例化相应对象之前对BeanDefinition
信息做最后的修改,相当于启动阶段的最后一步。
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
对于BeanFactory容器,需要手动调用BeanFactoryPostProcessor
的postProcessBeanFactory()
方法将其应用到容器,对于ApplicationContext容器,则会自动识别配置文件中的BeanFactoryPostProcessor
类并应用它。
spring提供了下面几个实现类:
- PropertyPlaceholderConfigurer:允许我们用占位符
${}
在properties文件中加载一些变量,有三个模式,可以通过setSystemPropertiesMode
来变更,默认为SYSTEM_PROPERTIES_ MODE_FALLBACK
,如果找不到所需要的属性会在System Properties中寻找(即启动命令中指定的property)<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>properties_path</value> </list> </property> </bean>
- PropertyOverrideConfigurer:对容器中已定义的bean的属性进行替换,通过配置文件
- CustomEditorConfigurer:配置从字符串到具体对象转换过程中的信息,如日期等
Bean的作用域
Bean是有作用域的,超出作用域的Bean将被销毁,对应配置文件中bean的scope
属性。非Web容器中,Bean有两种作用域
- singleton:在spring中只存在一个bean实例,初始化后将一直存活到容器退出,与容器几乎相同的寿命。
- prototype:每次收到bean实例请求的时候都会重新生成一个新的实例对象,生成之后返回给请求方,容器即不再拥有对象的引用,请求方自己来负责对象实例生命周期的管理。
对于Web容器,还新增了request
、session
和global session
三种作用域,这里不做展开。
Bean的实例化
容器启动之后仅仅拥有对象的BeanDefinition来保存实例化对象需要的信息,Bean的实例化只有在请求BeanFactory
的getBean()
方法之后才进行,当然这一步可能是应用程序显式调用,也可能是由容器隐式调用的。(ApplicationContext容器就是在容器启动之后在refresh()
方法中隐式调用注册对象的getBean()
方法)。
不同于一般对象,Spring给其管理的对象以统一的生命周期,不再是「new完被使用,脱离作用域后被回收」的命运。Bean的生命周期如下所示:
1.实例化
容器采用策略模式实例化对象,这一步只是进行简单的初始化,并未进行依赖注入。返回BeanWarpper
对象而非对象实例,为了方便第二步的属性设置。
InstantiationStrategy
接口定义了实例化策略,CglibSubclassingInstantiationStrategy
是其默认的实现类,采用CGLIB动态字节码生成功能。
2.设置属性及依赖
根据BeanDefinition中保存的信息对第一步返回的BeanWrapper
对象调用setPropertyValue()
方法设置属性和依赖对象,即IoC注入。
BeanWrapper
接口间接继承了PropertyAccessor
、PropertyEditorRegistry
和TypeConverter
,提供了一系列类型转换和设置对象属性的方法。
3.检查Aware接口并设置相关依赖
容器会检查实例对象是否实现了相关Aware接口,如果是,会为对象注入相关的依赖。对于BeanFactory容器有下面三个Aware接口:
BeanNameAware
:将配置文件中bean的name属性注入实例对象BeanClassLoaderAware
:将bean的ClassLoader注入实例对象,默认为org.springframework.util.ClassUtils
类的ClassloaderBeanFactoryAware
:将bean的BeanFactory容器引用注入实例对象
对于ApplicationContext容器,则新增了下面几个Aware方法,都会将容器自身引用注入到对象实例,不同于上面几个方法的实现机理,下面的方法是通过BeanPostProcessor
方式实现的,之后会提到。
ResourceLoaderAware
ApplicationEventPublisherAware
MessageSourceAware
ApplicationContextAware
4.BeanPostProcessor
为实例注册一个处理器,与之前的BeanFactoryPostProcessor
接口类似,对于BeanFactory容器,需要手动调用容器的addBeanPostProcessor()
方法将其实现类应用到容器,而ApplicationContext则可以自动识别配置文件中配置的BeanPostProcessor
的实现类并将其应用到容器。
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
BeanPostProcessor
接口定义了两个方法,分别在下面要提到的InitilizationBean
和init-method
的之前和之后进行,称作前置处理和后置处理。这两个接口将Bean实例作为参数传入,因此可以对Bean的属性进行任意操作。
ApplicationContextAwareProcessor
就是其中一个实现类,其postProcessBeforeInitialization()
方法会检查并设置Aware依赖。private void invokeAwareInterfaces(Object bean) { if (bean instanceof Aware) { if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware) bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext); } } }
5.InitializationBean和init-method
在前置处理结束之后容器会检查对象是否实现了InitializationBean
接口,这个接口只定义了一个方法afterPropertiesSet()
,由于没有传入Bean实例,因此只能进行一些额外的逻辑而不能对Bean对象属性进行操作。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
这个接口对业务对象有侵入性,spring为配置文件中的bean提供了一个init-method
属性,可以为对象设置初始化方法,作用与afterPropertiesSet()
类似。
6.DisposableBean与destory-method
在Bean实例被销毁之前会检查对象是否实现了DisposableBean
接口,同初始化时类似,spring为bean添加了destory-method
属性,为对象提供了自定义销毁的逻辑。需要注意的是,如果是prototype
作用域下的实例,则无法执行自毁逻辑。
public interface DisposableBean {
void destroy() throws Exception;
}
容器需要明确指定这些自定义销毁的逻辑在什么时候被执行,否则实例将不会执行这些自毁逻辑而直接被销毁。
- 对于BeanFactory容器,需要手动执行容器的
destroySingletons()
方法销毁容器中管理的所有singleton类型的对象实例。 - 对于ApplicationContext容器,则需要调用容器的
registerShutdownHook()
方法执行bean的自毁逻辑,该方法会调用Runtime
类的addShutdownHook()
方法调用相应bean的销毁逻辑。
基于注解的注入
spring2.5之后提供了基于注解的注入方式,实际上还是采用BeanPostProcessor
的方式,容器应用了特定的BeanPostProcessor
,就可以使用相应的注解实现注入。
AutowiredAnnotationBeanPostProcessor
:
- Autowired:按照类型匹配,可以实现属性、构造器或者方法的自动注入
- Qualifier:当容器内找到多个相同类型的实例时,需要用Qualifier指定bean的
id
或者name
属性
CommonAnnotationBeanPostProcessor
:
- Resource:基于
BeanName
的查找方式实现自动注入 - PostConstruct:类似于
init-method
和BeanInitialization
- PostDestroy:类似于
destroy-method
和DisposableBean
上面两个BeanPostProcessor
指定了容器中Bean的依赖关系,配置文件中的<context:annotation-config/>
可以实现上面两个BeanPostProcessor
的注册应用,不仅如此,它还同时应用了PersistenceAnnotationBeanPostProcessor
和RequiredAnnotationBeanPostProcessor
。
要实现Bean的自动装载,将其从配置文件中消失,则需要依赖@Component
等注解,通过classpath-scanning功能实现,在容器启动时,它会从某一顶层(base package)开始扫描,提取相应注解的类的BeanDefinition信息,构建相应的BeanDefinition并将其添加进容器。不但如此,它还会将上面提到的两种BeanPostProcessor
应用到容器中,满足依赖注入的需求。
<context:component-scan base-package="org.mypackage"/>
ApplicationContext
相比于BeanFactory,ApplicationContext进一步扩展了基本容器的功能外(包括对BeanFactoryPostProcessor
、BeanPostProcessor
及其他特殊类型bean的自动识别,容器启动后自动初始化实例bean),还提供了其他更为先进的功能。ApplicationContext常用的实现类主要有下面三个:
FileSystemXmlApplicationContext
:默认从文件系统加载bean及相关资源。ClassPathXmlApplicationContext
:默认从classpath加载bean及相关资源。XmlWebApplicationContext
:基于Web应用程序。
下面是ApplicationContext特有的功能,这里不做展开
- 统一资源定位
- 国际化信息支持
- 容器内事件发布
- 多模块加载的简化
(End)
参考资料:
- spring文档翻译
- 《Spring揭秘》