Posts tagged Spring
Spring IoC之容器扩展点
0容器扩展点
Spring中容器的扩展点一共有三个地方可以扩展,分别对应于三个接口:BeanPostProcessor,BeanFactoryPostProcessor和FactoryBean
BeanPostProcessor
BeanPostProcessor:这个接口可以使Bean在初始化前面或者后面对Bean实例作出修改,下面来看一个例子,作用是仅仅打出Bean的名字以及信息,首先需要在实现BeanPostProcessor接口:
public class TracingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean:" + beanName + " ---- " + bean.toString());
return bean;
}
}
然后将这个BeanPostProcessor定义到Bean配置信息里面去:
<bean class="com.alibaba.khotyn.test.TracingBeanPostProcessor"/>
这样在容器初始化一个Bean之后,容器就会调用TracingBeanPostProcessor将Bean的信息打印到控制台上。注意这里面配置的BeanPostProcessor甚至没有BeanName。
BeanFactoryPostProcessor
BeanFactoryPostProcessor和前面的BeanPostProcessor在名字上看上去很像,在功能上也是类似的,区别的地方在于BeanFactoryPostProcessor可以对Bean定义信息的元数据进行修改(BeanDefinition),而后者是在Bean初始化的时候可以对Bean实例进行修改,可以说BeanFactoryPostProcessor修改地更为彻底一些。在Spring中,已经有一些类实现了BeanFactoryPostPorcessor来提供一些功能,比如PropertyPlaceholderConfigurer,可以对用Property文件中的属性替换XML文件中的属性,下面是一个简单的例子:
<bean id="beanA" class="com.alibaba.khotyn.test.BeanA">
<property name="name" value="${name}"></property>
<property name="address" value="${address}"></property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>configure.properties</value>
</list>
</property>
</bean>
这样,Properties中的属性就可以去替换XML中相应的属性了
FactoryBean
这个接口乍看可能不怎么像容器的扩展点。但是考虑到最终的Bean使FactoryBean生产出来,所以Bean的初始化完全掌控在FactoryBean当中,这个接口的确是容器的一个扩展点。
Spring IoC之Bean生命周期管理
0Bean的生命周期管理
Spring的Bean容器只管理非单例Bean的生命周期,单例Bean的生命周期不在管理范围内。用一张很老的图来展示下Bean的生命周期管理

关于这张图里面我们可以主要关注3个类:
- BeanPostProcessor:这个接口定义了两个方法,分别是:postProcessBeforeInitialization和postProcessAfterInitialization,分别在初始化Bean之前和之后被调用,通过这两个方法,接口的实现者可以修改Bean的定义信息。如果接口的实现者需要定制多个BeanPostProcessor,那么它也需要实现Ordered接口。和这个类的非常相似的一个类是BeanFactoryPostProcessor,这两个类的一个区别使后者操纵的使Bean配置信息的元数据,这就意味着Spring允许在容器初始化Bean之前去修改Bean配置信息的元数据。这个接口的一个实现就是PropertyPlaceholderConfigurer和PropertyOverrideConfigurer,具体的例子可以参考Spring Reference
- InitializingBean:这个接口定义了一个afterPropertiesSet()方法,这个方法在Bean的属性被全部设置后发出。
- DesposableBean:这个接口定义了一个destroy()方法,这个方法在可以在Bean销毁时调用,比如可以做释放资源用。
Spring IoC之获取Bean
1非单例Bean的获取
Spring Bean获取的主体代码在AbstractBeanFactory的doGetBean方法中,看下面的方法:
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 转换BeanName,如果name参数传过来是aliasName的话,在这里会根据aliasName取出真正的BeanName
final String beanName = transformedBeanName(name);
Object bean;
// 首先检查单例缓存里面有没有这个Bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 将取出的object(可能是一个FactoryBean)转换成Bean的类型
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 检查是否已经在创建中,如果正在创建中,就确定存在循环引用
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 检查ParentBeanFactory是否存在Bean定义信息
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 如果ParentBeanFacotory不为空而且此BeanFactory不包含Bean定义信息
String nameToLookup = originalBeanName(name);
if (args != null) {
// 如果参数不为空,带上参数将获取Bean委托给ParentBeanFactory
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// 如果参数为空,则将获取Bean委托给ParentBeanFactory的标准getBean方法
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// typeCheckOnly,实例用来做类型检测的,而不是真正地拿来用的
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
// 如果Bean的定义信息在子Bean定义信息中,则合并到当前的Bean定义信息中
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 处理depends on的情况,确保当前Bean依赖的Bean先创建
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// 开始创建Bean实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
elseif (mbd.isPrototype()) {
// Bean的Scope是Prototype,创建一个新实例
Object prototypeInstance = null;
try {
// 在Bean创建之前,默认行为是:将当前Bean标记为正在创建中,用于前面检测循环引用
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
// 在Bean创建之后,默认行为是:将当前Bean标记为不在创建中,用于前面检测循环引用
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
// 检测Bean实例和需要的类型是否匹配
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}
从这里可以看出,Spring是首先尝试从单例缓存中获取Bean,如果找不到,然后在根据不同的Bean Scope调用不同的方法去创建Bean。
Spring IoC之容器初始化
0容器初始化
Spring容器的初始化主要分为三个步骤,分别使Bean信息的定位,解析与注册。以XML文件形式的Bean信息为例,Spring首先找到XML文件的位置,然后读取此文件,并且将文件解析成相应的BeanDefinition,最后将获取到的BeanDefinition注册到BeanFactory中去。
Bean信息定位,解析与注册
Spring容器的初始化的主要工作是定位Bean配置文件,读取并解析Bean配置文件,最后生成相应的BeanDefinition,先看一张简单的序列图对Spring容器的初始化做大概的了解:

从这副顺序图中我们可以看出,Spring容器的初始化过程主要由四个类来完成,我们可以参考下类的注释以了解这几个类的职责:
- XmlBeanDefinitionReader:用于XML Bean定义信息的BeanDefinitionReader,将事实上的XML文件读取代理给BeanDefinitionDocumentReader接口的一个实现。
- BeanDefinitionDocumentReader:用于解析Spring Bean定义信息的服务提供接口(SPI),在实际解析DOM文档的时候,由XmlBeanDefinitionReader来调用。
- BeanDefinitionParserDelegate:用于解析XML Bean定义信息的有状态代理类。负责一个Bean的定义信息解析策划能够BeanDefinition对象。
- BeanDefinitionRegistry:负责将得到的Bean定义信息注册到BeanFactory中。
Spring IoC 概览
4主要的设计思想
关于Spring设计的主要设计思想,其实Rod Johnson在他的那本《Expert One on One – J2EE Development without EJB》中有详细的解释。这里做下概括,Spring出现的主要原因是为了提供一个轻量级的容器,这个容器除了提供容器的基本服务以外,还应该具备以下的特点:
- 容器应该能够管理在其中的应用程序代码,但是不应该将容器侵入到应用程序的代码之中。也就是说,应用程序的代码应该是独立于容器的
- 容器应该能够快速启动
- 在容器中部署一个对象应该不需要任何特殊的部署步骤
- 容器应该依赖于最少的API以能够在不同的环境下运行
- ……..
而IoC正是为了解决以上的第一个特点的,在IoC中,组件不需要去查找它所依赖的对象,而是由容器来负责将组件所依赖的对象通过JavaBean Properties(Setter方法)或者构造函数来注入给组件。
主要类及其职责

这个类图相对来说比较简单些,主要包括了继承BeanFactory接口的各个接口及其实现,主要目的是为了摸清BeanFactory这一条线里面各个类之间的关系。整个类的继承体系相对比较简单,其中有几个类/接口的作用需要重点把握:
- BeanFactory:这个接口的作用是定义了访问Bean容器所需的基本方法,是整个Spring Bean容器的底层接口
- AbstractBeanFactory:这个类的作用从名字上就可以看出,使BeanFactory的一个抽象实现,提供了诸如getBean等方法的默认实现
- SingletonBeanRegistry:这个接口的主要作用对单例Bean进行管理。
- 其他的一些类/接口基本上是对BeanFactory接口进行了扩展,主要的功能可以看类/接口的名字或者注释来了解
感想
从Spring的类图中可以看出,Spring的类的命名的非常好,基本上让人一下就看出了这个类的职责所在,并且这个职责基本上是单一的,不会有一个类去做多件事情。Spring对BeanFactory的扩展接口也是只加了一种特性,然后通过实现多个接口以实现多个特性,这样的设计使接口的实现方可以方便的根据需求挑选接口来实现,而不用担心实现过多的功能。