通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Sping的IoC容器在完成这些底层工作的基础上,还提供了Bean实例缓存,生命周期管理,Bean实例代理,事件发布,资源装载等高级服务。
Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同类型的Java对象成为可能,应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。我们一般称BeanFactory为IoC容器,而称ApplicationContext为应用上下文。但有时为了行文方便。
我们可以进行简单划分:
BeanFactory是Sping框架的基础设施,而面向sping本身;ApplicationContext面向使用Sping框架的开发者,几乎所有的应用场合我们都是直接使用ApplicationContet而非底层的BeanFactory。
BeanFactory介绍
诚如其名,BeanFactory是一个类工厂,但和传统的类工厂不同,传统的类工厂仅负责构造一个或几个类的实例。而BeanFactory是类的通用工厂,它可以创建并管理各种类的对象。这些可被创建和管理的对象本身没有什么特别之处,仅是一个POJO。Sping称这些被创建和管理的Java对象为Bean。我们知道JavaBean是要满足一定规范的,如必须提供一个默认不带参的构造函数,不依赖于某一特定的容器等,但Spring中所说的Bean比JavaBean更宽泛一些,所有可以被Sping容器实例化并管理的Java类都可以成为Bean。
BeanFactory的类体系结构
Spring为BeanFactory提供了多种实现,最常用的是XmlBeanFactory。XmlBeanFactory的类继承体系设计优雅,堪称经典。通过继承体系,我们可以很容易了解到XmlBeanFactory具有哪些功能:BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean,BeanFactory的功能通过其他的接口得到不断扩展。下面对上图涉及到的其他接口分别进行说明。
- ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法,如查看Bean的个数,获取某一个类型Bean的配置名,查看容器中是否包括某一Bean等方法;
- HierarchicalBeanFactory:父子级联IoC容器的接口,子容器可以通过接口方法访问父容器;
- ConfigurableBeanFactory:是一个重要的接口,增强了IoC容器的可定制性,它定义了设置类装载器,属性编辑器,容器初始化后置处理器等方法;
- AutowireCapableBeanFactory;
初始化BeanFactory
下面,我们使用sping配置文件为Car提供配置信息,然后通过BeanFactory装在配置文件,启动sping IoC容器。sping配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car" class="com.hhxs.bbt.web.Car"
p:brand="红旗CA72"
p:color="黑色"
p:maxSpeed="200" />
</beans>
下面我们通过XmlBeanFactory实现类启动sping IoC容器:
public class BeanFactoryTest {
public static void main(String[] args) throws Throwable{
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource res = resolver.getResource("classpath:spring/beans.xml");
System.out.println(res.getURL());
BeanFactory bf = new XmlBeanFactory(res);
System.out.println("init BeanFactory.");
Car car = bf.getBean("car",Car.class);
System.out.println("car bean is ready for use!");
car.introduce();
}
}
XmlBeanFactory通过Resource装装载sping配置信息并启动IoC容器,然后就可以通过BeanFactory#getBean(beanName)方法从IoC容器中获取Bean了。通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一个调用时。对于单实例(singleton)的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean()获取Bean时将直接从IoC容器的缓存中获取Bean实例。
sping在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例Bean的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。
值得一提的是,在初始化BeanFactory时,必须为其提供一种日志框架,我们使用Log4J,即在类路径下提供Log4J配置文件,这样启动Sping容器才不会报错。
ApplicationContext介绍
如果说BeanFactory是Sping的心脏,那么ApplicationContext就是完整的身躯了。ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在Beanfactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置的方式实现。
ApplicationContext类体系结构
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件,我们了解一下ApplicationContext的类继承体系:
从上图中我们可以看出ApplicationContext继承了hierarchicalBeanFactory和ListableBeanFactory接口,在此基础上,还通过多个其他的接口扩展了BeanFactory的功能,这些接口包括:
- ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件,关闭事件等。实现了ApplicationListener事件监听接口的Bean可以接收到容器事件,并对事件进行响应处理。在ApplicationContext抽象实现类AbstractApplicationContext中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者。
- MessageSource:为应用提供i18n国际化消息访问的功能;
- ResourcePatternResolver:所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver的功能,可以通过带前缀的Ant风格的资源文件路径装载sping的配置文件。
- LifeCycle:该接口是Sping2.0加入的,该接口提供了start()和stop()两个方法,主要用于控制异步处理过程。在具体使用时,ApplicationContext及具体的Bean都必须同时该接口,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,已达到管理和控制JMX,任务调度等目的。
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。ApplicationContext的初始化和BeanFactory有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean时才实例化目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean。因此ApplicationContext的初始化时间会比BeanFactory稍长一些,不过稍后的调用则没有“第一次惩罚”问题。
spring3.0支持基于类注解的配置方式,主要功能来自于sping的名为JavaConfig子项目,目前JavaConfig已经升级为spring核心框架的一部分。一个标注@Configuration注解的POJO即可提供sping所需的Bean配置信息:
package com.yeren;
public class Car {
private String brand;
private String color;
private int maxSpeed;
public Car(){}
public Car(String brand,String color,int maxSpeed){
this.brand=brand;
this.color=color;
this.maxSpeed=maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public void introduce(){
System.out.println("brand:"+brand+";color:"+color+";maxSpeed:"+maxSpeed);
}
}
package com.yeren;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.awt.*;
@Configuration
public class BeanA {
@Bean(name="car")
public Car buildCar(){
Car car=new Car();
car.setBrand("红旗CA72");
car.setMaxSpeed(200);
return car;
}
}
package com.yeren;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* Hello world!
*
*/
public class App
{
public static void main(String []args)throws Throwable{
ApplicationContext ctx=new AnnotationConfigApplicationContext(BeanA.class);
Car car=ctx.getBean("car",Car.class);
System.out.println();
}
}
和基于XML文件配置方式的优势在于,类注解的配置方式可以很容易地让开发者控制Bean的初始化过程,比基于XML的配置更加灵活。sping为基于注解类的配置提供了专门的ApplicationContext实现类:AnnotationConfigApplicationContext。
WebApplicationContext类体系结构
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContextde引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Sping应用上下文。sping专门为此提供一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc)方法,即可以从ServletContext中获取WebApplicationContext实例。
由于Web应用比一般的应用拥有更多的特性,因此WebApplicationContext扩展了ApplicationContext。WebApplicationContext定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动时,WebApplicationContext实例即以此为键放置在ServletContext的属性列表中,因此我们可以直接通过以下话语从Web容器中获取WebApplicationContext:
WebApplicationContext wac=(WebApplicationContext)servletContext.getAttribute(ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
这正是我们前面提到的WebApplicationContextUtils工具类getWebApplicationContext(ServletContext sc)方法的内部实现方式。这样sping的web应用上下文和web容器的上下文就可以实现互访,二者实现了融合:
ConfigurableWebApplicationContext扩展了WebApplicationContext,它允许通过配置的方式实例化WebaApplicationContext,它定义了两个重要的方法:
- setServletContext(ServletContext servletContext):为sping设置web应用上下文,以便两者整合;
- setConfigLocations(String[] configLocations):设置sping配置文件地址,一般情况下,配置文件地址是相对于web根目录的地址,如/WEB-INF/baobaotao-dao.xml,/WEB-INF/baobaotao-service.xml等。但用户也可以使用带资源类型前缀的地址,如classpath:com/baobaotao/beans.xml等。
WebApplicationContext初始化
WebApplicationContext的初始化方式和BeanFactory,ApplicationContext有所区别,因为WebApplicationContext需要Servlet或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个,我们就可以完成启动sping web应用上下文的工作。
sping分别提供了用于启动WebApplicationContext的Servlet和Web容器监听器: - org.spingframework.web.context.ContextLoaderServlet;
- org.spingframework.web.context.ContextLoaderListener。
两者的内部都实现了启动WebApplicationContext实例的逻辑,我们只要根据Web容器的具体情况选择两者之一,并在web.xml中完成配置就可以了。
父子容器
通过HierarchicalBeanFactory接口,sping的IoC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但父容器不能访问子容器的Bean。在容器内,Bean的id必须是唯一的,但子容器可以拥有一个和父容器id相同的Bean。父子容器层级体系增强了sping容器架构的扩展性和灵活性,因为第三方可以通过编程的方式,为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。