Spring核心之FactoryBean,BeanFactory,ApplicationContext的区别

1 FactoryBean和BeanFactory区别

在 Spring 中,有两个接口:BeanFactoryFactoryBean 因为名字相近很容易被混淆。那他们之间有什么区别呢

1.1 BeanFactory

1.1.1 定义

BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂
BeanFactory定义了IOC容器的最基本形式,并提供了IOC容器应遵守的的最基本的接口,也就是Spring IOC所遵守的最底层最基本的编程规范。
Spring中,BeanFactoryIOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖

Spring代码中,BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,比如:

  • DefaultListableBeanFactory。
  • XmlBeanFactory
  • ApplicationContext

这些实现类从不同的维度对 beanFactory 进行了扩展。

1.1.2 beanFactory 源码

package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;

public interface BeanFactory {

    factoryBean 的转义标识符。(具体用法后面有介绍)
    String FACTORY_BEAN_PREFIX = "&";
        
    根据 name 从容器中拿对应的 bean
    Object getBean(String name) throws BeansException;
    
    根据 name 和 type 从容器中拿对应的 bean,要对 bean 的类型做校验。
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
    在容器中能否找到与 name 匹配的 bean 或者 beanDefinition。
    boolean containsBean(String name);
    
    判断 name 对对应的 bean 是不是 单例。
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    判断 name 对应的 bean 与指定的类型是否匹配。
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
    根据 name 获取对应的 bean 的类型。
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
    根据 name 获取对应 bean 的 别名。
    String[] getAliases(String name);
}

1.1.3 BeanFactory体系结构

BeanFactory 的类体系结构

  • BeanFactory: 接口位于类结构树的顶端, 它最主要的方法就是getBean(StringbeanName),该方法从容器中返回特定名称的BeanBeanFactory 的功能通过其他的接口得到不断扩展。
  • ListableBeanFactory:该接口定义了访问容器中Bean 基本信息的若干方法,如查看Bean 的个数、获取某一类型Bean 的配置名、查看容器中是否包括某一Bean 等方法;
  • HierarchicalBeanFactory:父子级联IoC 容器的接口,子容器可以通过接口方法访问父容器;
  • ConfigurableBeanFactory:是一个重要的接口,增强了IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法;
  • AutowireCapableBeanFactory:定义了将容器中的Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法;
  • SingletonBeanRegistry:定义了允许在运行期间向容器注册单实例Bean 的方法;
  • BeanDefinitionRegistrySpring 配置文件中每一个<bean>节点元素在Spring 容器里都通过一个BeanDefinition对象表示,它描述了Bean的配置信息。而BeanDefinitionRegistry 接口提供了向容器手工注册BeanDefinition 对象的方法

1.1.4 使用场景

使用场景:

  • 通过 名字或类型从容器中获取 bean。
  • 判断容器中是否包含指定的 bean。
  • 判断 bean 是不是单例。

1.2 FactoryBean

一般情况下,Spring通过反射机制利用<bean>class属性指定实现类实例化Bean,但在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

首先 FactoryBean 是一个 bean,但它又不仅仅是个 bean。它是一个可以 创建修饰 其他对象的工厂 bean,这跟设计模式中的工厂模式或者装饰模式很相似,它可以创建除自身以外的其他对象

1.2.1 FactoryBean源码

public interface FactoryBean<T> {   
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";   
    
    从 factory 中获取 bean
    @Nullable
    T getObject() throws Exception;
    从 beanFactory 中获取类型
    
    @Nullable
    Class<?> getObjectType();
    
    判断是单例?
    default boolean isSingleton() {
        return true;
    }    
}

从上面的接口可以看出,FactoryBean 有工厂的味道。也就是说,如果一个 A类实现了 FactoryBean 接口,那么 类A 就变成了 A工厂。根据 A 的名称获得的实际上是工厂调用 getObject() 返回的对象, 而不是 A工厂自己。如果想获得 A工厂自己的实例,需要添加 & 前缀。

1.2.2 用个demo解释下

这是一个简单的 FactoryBean 的使用

package com.example.demo.domian;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class MyFactoryBean implements FactoryBean { 
  // 保存一句话,用来区分不同的对象。  
  private String message;
  // 无参构造器。
  public MyFactoryBean() {
      // 意思是:当前对象是 MyFactoryBean 的对象。
    this.message = "object of myFactoryBeanSelf";
  }
  // 有参构造器。
  public MyFactoryBean(String message) {
    this.message = message;
  }
  // 获取 message。
  public String getMessage() {
    return this.message;
  }

  @Override
  /**
   *  这个方法在执行时创建了新的 MyFactoryBean 类型的对象。
   *  这里继续沿用了 MyFactoryBean 类型,但是可以是别的类型
   *  比如:Person、Car、等等。
   */
  public Object getObject() throws Exception {
      // 意思是:当前对象是 MyFactoryBean 的 getObject() 创建的。
    return new MyFactoryBean("object from getObject() of MyFactoryBean");
  }

  @Override
  public Class<?> getObjectType() {
    return MyFactoryBean.class
  }
}

测试内容很简单,MyFactoryBean@Component 注解了,当启动SpringBoot程序的时候,会为 MyFactoryBean 构建 bean 并且保存到容器中。我们要测试的就是用 类: MyFactoryBean 的 名字: myFactoryBean 去容器中拿 bean,看拿到的结果是什么?

package com.example.demo.domian;


import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = com.example.demo.domian.MyFactoryBean.class)
class MyFactoryBeanTest {
  @Autowired
  private ApplicationContext context;

  @Test
  public void test() {
    // 第一次用 myFactoryBean 去拿。  
    MyFactoryBean myBean1 = (MyFactoryBean) context.getBean("myFactoryBean");
    System.out.println("myBean1 = " + myBean1.getMessage());
    // 第二次用 &myFactoryBean 去拿。  
    MyFactoryBean myBean2 = (MyFactoryBean) context.getBean("&myFactoryBean");
    System.out.println("myBean2 = " + myBean2.getMessage());、
    // 判断两次拿到的对象是不是一样的?    
    System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
  }
}
测试结果:
myBean1 = object from getObject() of MyFactoryBean
myBean2 = object of myFactoryBeanSelf
myBean1.equals(myBean2) = false

结果很明显:
第一次使用 myFactoryBean 去容器中拿,实际上是容器中 MyFactorybeanbean 调用了 getObject()方法,并将结果返回。
第二次使用 &myFactoryBean 去容器中拿,才是真正拿到了 MyFactorybean 的 bean。
两次拿出来的对象当然是不一样的。
& 就是用于区分到底拿谁的的前缀。

1.2.3 使用场景

为什么需要 FactoryBean ,它的特殊功能是什么呢?
SpringFactoryBean最大的应用场景是用在 AOP 中。我们都知道,AOP 实际上是 Spring 在运行是创建出来的代理对象,这个对象是在运行时才被创建的,而不是在启动时定义的,这与工厂方法模式是一致的。更生动地说,AOP 代理对象通过 java 反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中。Spring 中的 ProxyFactoryBean 就是干这事的。
因此,FactoryBean 提供了更灵活的实例化 bean 的方法。通过 FactoryBean 我们可以创建更复杂的 bean

1.3 区别总结

  • BeanFactoryIOC 容器,并且提供方法支持外部程序对这些 bean 的访问,在程序启动时 根据传入的参数产生各种类型的 bean,并添加到 IOC容器(实现 BeanFactory 接口的类) 的 singletonObject 属性中。
  • FactoryBean: 首先是个 bean,也存放在 BeanFactory 中。它具有工厂方法的功能,在程序运行中 产生指定(一种)类型的 bean,并添加到了 IOC容器中的 factoryBeanObjectCache 属性中。
    所以,这两种方式创建的 bean 都是被 spring 容器管理的
  • BeanFactoryFactoryBean都可以用来创建对象,只不过创建的流程和方式不同
    • 当使用BeanFactory的时候,必须要严格的遵守bean的生命周期,经过一系列繁杂的步骤之后可以创建出单例对象,是流水线式的创建过程
    • FactoryBean是用户可以自定义bean对象的创建流程,不需要按照bean的生命周期来创建

转载于:https://blog.csdn.net/yy_diego/article/details/115710104

2 BeanFactory和ApplicationContext区别

BeanFactory已经在上面说过了,下面就不再赘述了

2.1 ApplicationContext

2.1.1 和BeanFactory关系

BeanFactoryApplicationContext 接口及其子类图

image.png

2.1.2 定义

继承了BeanFactory接口,拥有BeanFactory的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。

创建ApplicationContext的方法:

  • 从类路径下加载配置文件:ClassPathXmlApplicationContext("applicationContext.xml");
  • 从硬盘绝对路径下加载配置文件: FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”);

2.1.3 ApplicationContext类体系结构

Bean 工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IOC的配置机制。
应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory 基础之上。
几乎所有的应用场合我们都直接使用ApplicationContext 而非底层的BeanFactory

ApplicationContextBeanFactory 派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext 中则可以通过配置的方式实现。
ApplicationContext 的主要实现类是ClassPathXmlApplicationContextFileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中装载配置文件。

核心接口包括:

  • ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。实现了ApplicationListener事件监听接口的Bean 可以接收到容器事件, 并对事件进行响应处理。在ApplicationContext抽象实现类AbstractApplicationContext中,我们可以发现存在一个ApplicationEventMulticaster,它负责保存所有监听器,以便在容器产生上下文事件时通知这些事件监听者
  • MessageSource:为应用提供i18n国际化消息访问的功能;
  • ResourcePatternResolver : 所有ApplicationContext实现类都实现了类似于PathMatchingResourcePatternResolver的功能,可以通过带前缀的Ant 风格的资源文件路径装载Spring 的配置文件。
  • LifeCycle:该接口是Spring 2.0 加入的,该接口提供了start()stop()两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext实现及具体Bean实现,ApplicationContext会将start/stop的信息传递给容器中所有实现了该接口的Bean,以达到管理和控制JMX、任务调度等目的。
  • ConfigurableApplicationContext 扩展于ApplicationContext,它新增加了两个主要的方法:refresh()close(),让ApplicationContext具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下,调用refresh()则清除缓存并重新装载配置信息,而调用close()则可关闭应用上下文。这些接口方法为容器的控制管理带来了便利.

代码示例:

ApplicationContext ctx =new ClassPathXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx =new FileSystemXmlApplicationContext("com/baobaotao/context/beans.xml");
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"conf/beans1.xml","conf/beans2.xml"});

2.2 结论

早期的电脑性能低,内存小,所以Spring容器的容量不足,不能将所有的对象全部创建好放入容器,所以使用的是BeanFactory,需要某个对象时,再进行创建,随着电脑硬件的发展,内存越来越大,所以Spring框架引入了ApplicationContext,将所有的对象都创建好,放入容器,使用哪个对象,从容器中取得即可。
所以,web开发中,使用applicationContext. 在资源匮乏的环境可以使用BeanFactory

因此,ApplicationContext 的初始化和BeanFactory 有一个重大的区别:BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext则在初始化应用上下文时就实例化所有单实例的Bean

ApplicationContext接口,它由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先

转载于:https://blog.csdn.net/qq_20757489/article/details/88543252

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

推荐阅读更多精彩内容