上一篇:Note1
前言: 上一节展示了Spring通过面向POJO编程、 DI、 切面和模板技术来简化Java开发中的复杂性。 在这个过程中,展示了在基于XML的配置文件中如何配置bean和切面。但这些配置文件是如何加载的呢? 它们被加载到哪里去了?让我们再了解下Spring容器, 这是应用中的所有bean所驻留的地方。
1. Spring容器
【###】对于Spring容器,需要知道对象在容器中是如何被管理的,对象的整个生命周期经历了什么?
在基于Spring的应用中, 应用对象生存于Spring容器(container)中。 Spring容器负责创建对象, 装配它们, 配置它们并管理它们的整个生命周期, 从生存到死亡(在这里, 可能就是new到finalize()) 。
Spring容器并不是只有一个。 Spring自带了多个容器实现, 可以归为两种不同的类型:
> bean工厂(由org.springframework. beans.factory.BeanFactory接口定义) 是最简单的容器, 提供基本的DI支持。
> 应用上下文(由org.springframework.context.ApplicationContext接口定义) 基于BeanFactory构建, 并提供应用框架级别的服务 —— 例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。
虽然我们可以在bean工厂和应用上下文之间任选一种, 但bean工厂对大多数应用来说往往太低级了, 因此, 应用上下文要比bean工厂更受欢迎。 所以应该把精力集中在应用上下文的使用上, 而不再浪费时间讨论bean工厂。
2. Spring容器实现之——应用上下文
在上一篇文章(4.4节)中简单的说了下Spring怎样装载xml配置文件——Spring通过应用上下文(Application Context) 装载bean的定义并把它们组装起来。 Spring应用上下文全权负责对象的创建和组装。(组装:根据配置文件中对象之间的依赖关系,注入相应的依赖对象)
Spring自带了多种应用上下文的实现, 它们之间主要的区别仅仅在于如何加载配置。下面是几个常见的应用上下文:
> ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义, 把应用上下文的定义文件作为类资源。
> AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
> AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。
> XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。
> FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
其中,在后面讨论基于 Web 的 Spring 应用时再讲解 AnnotationConfigWebApplicationContext 和 XmlWebApplicationContext 这两个应用上下文。现在先了解下其他三个应用上下文。
2.1 ClassPathXmlApplicationContext / AnnotationConfigApplicationContext / FileSystemXmlapplicationcontext
无论是从文件系统中装载应用上下文还是从应用的类路径下的配置文件(或配置类)中装载应用上下文, 将bean加载到bean工厂的过程都是相似的:
//
ApplicationContext context = new FileSystemXmlapplicationcontext("c:/test.xml");
//
ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
//
ApplicationContext context = new AnnotationConfigApplicationContext(com.config.TestConfig.class);
使用FileSystemXmlApplicationContext和使用ClassPathXmlApp-licationContext的区别在于:
> FileSystemXmlApplicationContext在指定的文件系统路径(例如上述例子的c:/下)下查找径下查找knight.xml文件;
> 而ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件) 下查找 knight.xml文件。
2.2 应用上下文准备就绪之后, 我们就可以调用上下文的getBean()方法从Spring容器中获取bean
Test test = context.getBean(Test.calss);
3. Bean的生命周期
3.1 传统Java应用中Bean的生命周期
> 使用Java关键字new进行bean实例化, 然后该bean就可以使用了。
> 一旦该bean不再被使用,则由Java自动进行垃圾回收。
3.2 Spring应用中Bean的生命周期
bean在Spring容器中从创建到销毁经历了若干阶段, 每一阶段都可以针对Spring如何管理bean进行个性化定制 。
在bean准备就绪之前, bean工厂执行了若干启动步骤。让我们先对上图进行详细描述:
(1)Spring对bean进行实例化(说到底还是由Spring容器来new一个对象);
(2)Spring将值(值:例如,常量属性值)和bean的引用注入到bean对应的属性中;
(3)如果bean实现了 BeanNameAware 接口(必然也要重写该接口的方法: setBeanName()), Spring将bean的ID(配置文件中的<bean>的id属性值)传递给 setBeanName() 方法;
package org.springframework.beans.factory;
public interface BeanNameAware extends Aware {
void setBeanName(String name);
}
对上面的描述换个说法: 当Bean实现了 BeanNameAware 接口后,必然重写了 setBeanName(String name) 方法,当Spring容器走完第1、2步后就会将配置文件中该Bean的id值赋值给 setBeanName(String name) 方法的参数 name,这样在Bean中就拿到了该id值,然后该干嘛干嘛去。
(4)如果bean实现了 BeanFactoryAware 接口, Spring将调用 setBeanFactory() 方法, 将BeanFactory容器实例传入;
package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
public interface BeanFactoryAware extends Aware {
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
换个说法:和上面的解释类似,下面的也是同理。
(5)如果bean实现了 ApplicationContextAware接 口, Spring将调用 setApplicationContext() 方法, 将bean所在的应用上下文的引用传入进来;
package org.springframework.context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
public interface ApplicationContextAware extends Aware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
(6)如果bean实现了 BeanPostProcessor 接口, Spring将调用它们的 postProcessBeforeInitialization() 方法;
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
实现该接口后将要重写上面这两个方法,在此处会先调用预初始化方法 postProcessBeforeInitialization() ,如果bean没有实现 InitializingBean接口(也没有自定义初始化方法,即:init-method),则接着继续调用初始化后方法postProcessAfterInitialization() 。如果实现了 InitializingBean 接口(见下面),则会等调用完 InitializingBean 的 afterPropertiesSet() (如果有自定义初始化方法,还要再调用自定义初始化方法)方法后再调用 postProcessAfterInitialization() 方法。
(7)如果bean实现了 InitializingBean 接口, Spring将调用它们的 afterPropertiesSet() 方法。 类似地, 如果bean使用init-method声明了初始化方法(在Bean中可以自定义初始化和销毁方法,只要在配置文件中声明就可以生效了:<bean id="xx" class="xx.xxx" init-method="xxx" destroy-method="xxx">), 该自定义初始化方法会在 afterPropertiesSet() 方法之后被调用;
package org.springframework.beans.factory;
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
(8)此时, bean已经准备就绪, 可以被应用程序使用了, 它们将一直驻留在应用上下文中, 直到该应用上下文被销毁;
(9)如果bean实现了 DisposableBean 接口,Spring将调用它的 destroy() 接口方法。 同样,如果bean使用destroy-method声明了销毁方法, 该自定义销毁方法会在 destory() 方法之后被调用。
package org.springframework.beans.factory;
public interface DisposableBean {
void destroy() throws Exception;
}
4.小结
在上面的描述中简单的了解了如何创建和加载一个Spring容器,以及一个Bean在容器中生命周期。但是一个空的容器并没有太大的价值, 在你把东西放进去之前, 它里面什么都没有。为了从Spring的DI中受益, 我们必须将应用对象装配进Spring容器
中。这个将在后面再详细描述,在下一节主要来浏览一下Spring的体系结构, 了解一下Spring框架的基本组成部分和最新版本的Spring所发布的新特性。
下一篇:Note3