BeanFactory和ApplicationContext

  通过一个配置文件描述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的类体系结构

  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体系结构及与BeanFactory的关系

  从上图中我们可以看出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类体系结构与BeanFactory和ApplicationContext的关系

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容器的上下文就可以实现互访,二者实现了融合:

sping和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容器架构的扩展性和灵活性,因为第三方可以通过编程的方式,为一个已经存在的容器添加一个或多个特殊用途的子容器,以提供一些额外的功能。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容