百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

spring中的 BeanFactory 和 Application Context(被称为灵魂伴侣)

xsobi 2024-11-24 23:33 1 浏览

文中涉及的spring版本号为4.1.8.RELEASE;

BeanFactory接口

在spring容器中,BeanFactory接口是IOC容器要实现的最基础的接口,定义了管理bean的最基本的方法,例如获取实例、基本的判断等,如下图:


BeanFactory有多个子接口来进一步扩展bean相关的功能,以下两个与本文有直接关系,需要关注:

  • HierarchicalBeanFactory:beanFactory可以设置ParentBeanFactory,这个接口的两个方法都是和ParentBeanFactory相关的,getParentBeanFactory方法返回ParentBeanFactory,containsLocalBean方法是相对containsBean方法而言的,containsBean方法找不到bean的时候会通过ParentBeanFactory去找,而containsLocalBean只在当前beanFactory找;
  • ListableBeanFactory:主要用于获取bean相关信息,例如获取所有bean名称,查找指定类型的所有bean等,如下图:

ApplicationContext接口

关于ApplicationContext和BeanFactory的关系,看类图即可一目了然:


原来ApplicationContext继承了HierarchicalBeanFactory和ListableBeanFactory,也就是说前面看到的接口特性都被ApplicationContext继承下来了,另外通过类图可以发现,ApplicationContext还继承了诸如Environment、Resource、Message、Event
等相关的接口,也就是说除了bean的管理配置相关的能力,ApplicationContext还拥有了Environment(环境)MessageSource(国际化)ResourceLoader(资源)ApplicationEventPublisher(应用事件)等服务相关的接口,简单的说ApplicationContext是以bean管理为基础的综合能力扩展,用于满足业务对Spring综合能力的需要;

再看看ApplicationContext源码,除了继承,它自身也提供了一些扩展的能力:

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

    //标识当前context实例的id,最终会通过native方法来生成:System.identityHashCode
    String getId();

    //返回该context所属的应用名称,默认为空字符串,在web应用中返回的是servlet的contextpath 
    String getApplicationName();

    //返回当前context的名称
    String getDisplayName();

    //返回context第一次被加载的时间
    long getStartupDate();

    //返回该context的parent
    ApplicationContext getParent();

    //返回具有自动装配能力的beanFactory,默认返回的就是初始化时实例化的beanFactory
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

小结:

  • BeanFactory是基础,BeanFactory和它的子接口定义的API满足了spring环境中对bean管理和配置的需求;
  • ApplicationContext是扩展,以BeanFactory为主线,通过继承的方式综合了环境、国际化、资源、事件等多条支线,自己又规定了一些扩展服务(如返回context的id,应用名称等),而所有支线都以bean服务为基础;

实现类解析

如果有个类实现了ApplicationContext接口,就必须实现上述多个接口定义的方法,这个类岂不是很复杂?我们看看AbstractApplicationContext类的源码,这是个基础抽象类,常用的ClassPathXmlApplicationContext、AnnotationConfigServletWebServerApplicationContext等都继承自AbstractApplicationContext;

先看看是如何实现BeanFactory接口中定义的方法的:

    @Override
    public Object getBean(String name) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(name, requiredType);
    }

    @Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(requiredType);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(name, args);
    }

    @Override
    public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        assertBeanFactoryActive();
        return getBeanFactory().getBean(requiredType, args);
    }

    @Override
    public boolean containsBean(String name) {
        return getBeanFactory().containsBean(name);
    }

    @Override
    public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
        assertBeanFactoryActive();
        return getBeanFactory().isSingleton(name);
    }

    @Override
    public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
        assertBeanFactoryActive();
        return getBeanFactory().isPrototype(name);
    }

    @Override
    public boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException {
        assertBeanFactoryActive();
        return getBeanFactory().isTypeMatch(name, targetType);
    }

    @Override
    public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
        assertBeanFactoryActive();
        return getBeanFactory().getType(name);
    }

    @Override
    public String[] getAliases(String name) {
        return getBeanFactory().getAliases(name);
    }

看过上述代码后恍然大悟,原来AbstractApplicationContext并没有自己来完成bean的管理配置,而是全部委托给了getBeanFactory()方法返回的这个实例,接口是组合,实现也是组合,这种清晰的设计是学习典范;

再来看看getBeanFactory()方法,解释究竟是谁在真正的提供bean的管理配置服务,该方法的实现在AbstractApplicationContext的子类AbstractRefreshableApplicationContext中,代码很简单,返回了成员变量beanFactory:

public final ConfigurableListableBeanFactory getBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("BeanFactory not initialized or already closed - " +
                        "call 'refresh' before accessing beans via the ApplicationContext");
            }
            return this.beanFactory;
        }
    }

该成员变量在refreshBeanFactory方法中实例化,而refreshBeanFactory方法又是在spring容器初始化的时候调用的,所以容器初始化之后,AbstractApplicationContext就具备了bean管理配置的能力;

扩展思考

看了AbstractApplicationContext类中beanFactory相关的代码,我就猜想:莫非Message支线也是这个套路,委托给Message服务相关的实例来完成?看代码吧:

    @Override
    public String getMessage(String code, Object args[], String defaultMessage, Locale locale) {
        return getMessageSource().getMessage(code, args, defaultMessage, locale);
    }

    @Override
    public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
        return getMessageSource().getMessage(code, args, locale);
    }

    @Override
    public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
        return getMessageSource().getMessage(resolvable, locale);
    }

如上所示,果然如此,Message相关的接口实现,都是getMessageSource()方法返回的实例在承担,getMessageSource()方法返回的是成员变量messageSource,该成员变量也是在spring容器初始化的时候,调用initMessageSource方法生成的;

至此,通过查看源码,我们对spring的BeanFactory和ApplicationContext有了更清晰的认识,又经历了一次结合类图分析源码的实践,在学习spring的路上,希望本文能助您一臂之力,一起学习、共同进步;

相关推荐

js向对象中添加元素(对象,数组) js对象里面添加元素

一、添加一个元素对象名["属性名"]=值(值:可以是一个值,可以是一个对象,也可以是一个数组)这样添加进去的元素,就是一个值或对象或数组...

JS小技巧,如何去重对象数组?(一)

大家好,关于数组对象去重的业务场景,想必大家都遇到过类似的需求吧,这对这样的需求你是怎么做的呢。下面我就先和大家分享下如果是基于对象的1个属性是怎么去重实现的。方法一:使用.filter()和....

「C/C++」之数组、vector对象和array对象的比较

数组学习过C语言的,对数组应该都不会陌生,于是这里就不再对数组进行展开介绍。模板类vector模板类vector类似于string,也是一种动态数组。能够在运行阶段设置vector对象的长度,可以在末...

如何用sessionStorage保存对象和数组

背景:在工作中,我将[{},{}]对象数组形式,存储到sessionStorage,然后ta变成了我看不懂的形式,然后我想取之用之,发现不可能了~记录这次深刻的教训。$clickCouponIndex...

JavaScript Array 对象 javascript的array对象

Array对象Array对象用于在变量中存储多个值:varcars=["Saab","Volvo","BMW"];第一个数组元素的索引值为0,第二个索引值为1,以此类推。更多有...

JavaScript中的数组Array(对象) js array数组

1:数组Array:-数组也是一个对象-数组也是用来存储数据的-和object不同,数组中可以存储一组有序的数据,-数组中存储的数据我们称其为元素(element)-数组中的每一个元素都有一...

数组和对象方法&amp;数组去重 数组去重的5种方法前端

列举一下JavaScript数组和对象有哪些原生方法?数组:arr.concat(arr1,arr2,arrn);--合并两个或多个数组。此方法不会修改原有数组,而是返回一个新数组...

C++ 类如何定义对象数组?初始化数组?linux C++第43讲

对象数组学过C语言的读者对数组的概念应该很熟悉了。数组的元素可以是int类型的变量,例如int...

ElasticSearch第六篇:复合数据类型-数组,对象

在ElasticSearch中,使用JSON结构来存储数据,一个Key/Value对是JSON的一个字段,而Value可以是基础数据类型,也可以是数组,文档(也叫对象),或文档数组,因此,每个JSON...

第58条:区分数组对象和类数组对象

示例设想有两个不同类的API。第一个是位向量:有序的位集合varbits=newBitVector;bits.enable(4);bits.enable([1,3,8,17]);b...

八皇后问题解法(Common Lisp实现)

如何才能在一张国际象棋的棋盘上摆上八个皇后而不致使她们互相威胁呢?这个著名的问题可以方便地通过一种树搜索方法来解决。首先,我们需要写一个函数来判断棋盘上的两个皇后是否互相威协。在国际象棋中,皇后可以沿...

visual lisp修改颜色的模板函数 怎么更改visual studio的配色

(defunBF-yansemokuai(tuyuanyanse/ss)...

用中望CAD加载LISP程序技巧 中望cad2015怎么加载燕秀

1、首先请加载lisp程序,加载方法如下:在菜单栏选择工具——加载应用程序——添加,选择lisp程序然后加载,然后选择添加到启动组。2、然后是添加自定义栏以及图标,方法如下(以...

图的深度优先搜索和广度优先搜索(Common Lisp实现)

为了便于描述,本文中的图指的是下图所示的无向图。搜索指:搜索从S到F的一条路径。若存在,则以表的形式返回路径;若不存在,则返回nil。...

两个有助于理解Common Lisp宏的例子

在Lisp中,函数和数据具有相同的形式。这是Lisp语言的一个重大特色。一个Lisp函数可以分析另一个Lisp函数;甚至可以和另一个Lisp函数组成一个整体,并加以利用。Lisp的宏,是实现上述特色的...