BeanFactory、 ApplicationContext、FactoryBean区别与联系

BeanFactory

概述

Spring 作为一种IoC容器最大的目的就是降低组件与组件之间的依赖关系。而BeanFactory作为整个Spring IoC容器的核心使用了一种统一的方法来装配所有的应用对象,通过Java的反射机制和依赖注入来实现这一装配过程,使受之管理的对象无需知道Spring的存在,同时也降低了组件间的依赖程度,同时受管对象的生命周期由BeanFactory来统一管理,开发者就不用实现大量的自制的工厂Singleton。 

BeanFactory以Factory结尾,表示它是一个工厂类(接口),是负责生产和管理bean的一个工厂,同时也是IoC容器的核心接口, 

主要接口

接口分析详看:https://www.cnblogs.com/zrtqsk/p/4028453.html

BeanFactory接口位于类结构树的顶端,它最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean,BeanFactory的功能通过其他的接口得到不断扩展。下面对上图涉及到的其他接口分别进行说明。

接口说明

  1. XmlBeanFactory 读取XML配置文件(已弃用)
  2. ListableBeanFactory定义了访问容器中 Bean 的基本信息的若干方法,比如查看 Bean 的个数,获取某一类型的 Bean 的配置名称,或者查看容器中是否包含某一 Bean 等等方法。
  3. HierarhicalBeanFactory是父子级联 IoC 容器的接口,子容器可以通过接口方法访问父容器。
  4. ConfigurableBeanFactory是一个重要的接口,增强了 IoC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等等方法。
  5. AutowireCapableBeanFactory定义了将容器中的 Bean 按照某种规则,比如按照名称匹配,按照类型匹配等等来进行自动装配的方法。
  6. SingletonBeanRegistry定义了允许运行期间向容器注册单实例 Bean 的方法。
  7. BeanDefinitionRegistry在Spring 配置文件中每一个 Bean 的节点元素,在Spring 容器里面都通过一个 BeanDefinition 对象来表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 提供了向容器手工注册 BeanDefinition 对象的方法。

注意:BeanFactory 有众多的实现,在 Spring 3.2 之前的版本中,最常用的是 XmlBeanFactory,现已被废弃。官方建议使用 XmlBeanDefinitionReader 与 DefaultListableBeanFactory。

主要方法

方法解释

  1. containsBean(String beanName)判断工厂中是否包含给定名称的bean定义,若有则返回true
  2. getBean(String)返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
  3. getBean(String, Class)返回以给定名称注册的bean实例,并转换为给定class类型
  4. getType(String name)返回给定名称的bean的Class,如果没有找到指定的bean实例,则排除NoSuchBeanDefinitionException异常
  5. isSingleton(String)判断给定名称的bean定义是否为单例模式
  6. getAliases(String name)返回给定bean名称的所有别名

BeanFactory的初始化顺序

  1. 创建配置文件
  2. 装载配置文件
  3. 启动 IoC 容器
  4. 获取 Bean 实例(getBean())

上代码

代码结构

People.java

public class People {
    private String name;
    private int age;

    // 省略getter、setter

    @Override
    public String toString() {
        return "姓名:" + getName() + ",年龄:" + getAge();
    }
}

beans.xml

<?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.xsd">

    <bean id="zhangsan" class="demo.People"
          p:name="张三"
          p:age="23"
    />
</beans>

Main.java

public class Main {
    public static void main(String[] args) throws IOException {
        // 读取XML配置文件并启动容器
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("classpath:beans.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(resource);

        // 获取bean
        People people = factory.getBean("zhangsan", People.class);
        System.out.println(people.toString());
    }
}

运行结果

注意

1、通过 BeanFactory 启动 IoC 容器时,并不会初始化配置文件当中定义的 Bean,初始化的动作发生在第一次调用某个 Bean 的时候。对于单实例的 Bean 来说,BeanFactory 会缓存 Bean 的实例,所以第二次使用 getBean() 方法时,可以直接从 IoC 容器的缓存中来获取 Bean 的实例了。而 ApplicationContext 在初始化应用上下文时,就已经实例化所有单实例的 Bean。因此,ApplicationContext 的初始化时间会比 BeanFactory 的时间要稍微长一些。

2、在初始化 BeanFactory 时,必须为它提供一种日志框架。

扩展:实例化容器几种方法

网上搜到的很多教程都是用XmlBeanFactory来装载XML文件的,但是我上面的例子并没有用到。

因为XmlBeanFactory类从Spring 3.1开始已经被弃用

官方推荐使用DefaultListableBeanFactory和XmlBeanDefinitionReader

但这里还是列出XmlBeanFactory的两种方法

// 1通过文件路径找XML

// 1通过文件路径找XML
Resource resource = new FileSystemResource("D:\\demo\\src\\main\\resources\\beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

// 2通过classpath找XML

// 2通过classpath找XML
ClassPathResource resource = new ClassPathResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);

ApplicationContext

概述

ApplicationContextBeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式实现,而在ApplicationContext中则可以通过配置实现。

BeanFactorty接口提供了配置框架及基本功能,但是无法支持Spring的AOP功能和web应用。而ApplicationContext接口作为BeanFactory的派生,因而提供BeanFactory所有的功能。而且ApplicationContext还在功能上做了扩展,相较于BeanFactorty,ApplicationContext还提供了以下的功能: 

  1. MessageSource, 提供国际化的消息访问 
  2. 资源访问,如URL和文件  
  3. 事件传播特性,即支持aop特性
  4. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层 

继承关系

通过继承扩展了哪些功能?

如上图所示,ApplicationContext 继承了一些接口,通过这些接口扩展了 BeanFactory 的功能。这类接口包括:

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

主要子类

  1. ClassPathXmlApplicationContext从类路径加载配置文件
  2. FileSystemXmlApplicationContext从文件系统中装载配置文件
  3. AnnotationConfigApplicationContext可直接传入注解类,通过 register(Class…) 注册类或 scan(String…) 扫描路径 classpath 逐个注册类。

上代码

ApplicationContext支持两种模式

1、XML模式

2、注解模式

1、XML模式

代码结构

People.java

public class People {
    private String name;
    private int age;

    // 省略getter、setter

    @Override
    public String toString() {
        return "姓名:" + getName() + ",年龄:" + getAge();
    }
}

beans.xml

<?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.xsd">

    <!--注意:这里的class和BeanFactory的例子里面不一样,记得修改!-->
    <bean id="zhangsan" class="ACDemo.People"
          p:name="张三"
          p:age="23"
    />

</beans>

Main.java

public class Main {
    public static void main(String[] args) {
        // 从类路径加载XML
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        BeanFactory factory = context;
        People people = factory.getBean("zhangsan", People.class);
        System.out.println(people.toString());
    }
}

运行结果

加载XML几种方法

ApplicationContext 的主要实现类是:

  • ClassPathXmlApplicationContext - 从类路径加载配置文件。
  • FileSystemXmlApplicationContext - 从文件系统加载配置文件。

// 从类路径加载配置文件

// 从类路径加载配置文件
ApplicationContext context1 = new ClassPathXmlApplicationContext("beans.xml");

// 从文件系统加载配置文件

// 从文件系统加载配置文件
ApplicationContext context = new FileSystemXmlApplicationContext("D:/beans.xml");

还可以指定一组的配置文件,Spring 会自动将多个配置文件中的内容整合起来:

// 指定一组的配置文件

// 指定一组的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(new
        String[]{"beans.xml", "beans2.xml"});

Tips:ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext 也可以显式指定带资源类型前缀的路径。

2、注解模式

Spring从3.0开始支持基于类注解的配置方式,一个标注@Configuration注解的POJO即可提供Spring所需的Bean配置信息

和基于XML文件配置方式的优势在于,类注解的配置方式可以很容易地让开发者控制Bean的初始化过程,比基于XML的配置更加灵活。Spring为基于注解类的配置提供了专门的ApplicationContext实现类:AnnotationConfigApplicationContext。

代码结构

People.java

public class People {
    private String name;
    private int age;

    // 省略getter、setter

    @Override
    public String toString() {
        return "姓名:" + getName() + ",年龄:" + getAge();
    }
}

Beans.java

@Configuration
public class Beans {
    @Bean(name = "zhangsan")
    public People buildPeople() {
        People people = new People();
        people.setName("张三");
        people.setAge(23);
        return people;
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(Beans.class);
        People people = context.getBean("zhangsan", People.class);
        System.out.println(people.toString());
    }
}

运行结果

BeanFactory、ApplicationContext两者区别

概述

  1. ApplicationContext是BeanFactory的子类,并扩展了功能
  2. 初始化
  3. 延迟加载
  4. 手动、自动注册

1、ApplicationContext是BeanFactory的子类,并扩展了功能

可以看到,ApplicationContext继承了BeanFactory,BeanFactory是Spring中比较原始的Factory,它不支持AOP、Web等Spring插件,而ApplicationContext不仅包含了BeanFactory的所有功能,还支持Spring的各种插件,还以一种面向框架的方式工作以及对上下文进行分层和实现继承。

BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了更多面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext而不是底层的BeanFactory。

2、初始化

  1. BeanFactory在初始化容器时,并未实例化 Bean。直到 Bean 被调用时(调用getBean()方法)才会被实例化。对于单实例的 Bean 来说,BeanFactory 会缓存 Bean 的实例,所以第二次使用 getBean() 方法时,可以直接从 IoC 容器的缓存中来获取 Bean 的实例了。
  2. ApplicationContext在初始化应用上下文时,会实例化所有单实例的 Bean,所以相对来说,占用空间较大、初始化时间会比 BeanFactory 稍长。

3、延迟加载

BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。

举个例子:如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;

这样,我们就不能发现一些存在的Spring的配置问题。

ApplicationContext则相反,它是自身初始化的时候,就会一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。

4、手动、自动注册

BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。

FactoryBean

概述

Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,正常情况下根据从BeanFactory中获取的并不是这个类本身,而是FactoryBean的getBean()返回的对象,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。

为什么要使用FactoryBean?

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

有些情况我们需要自己的工厂类,通过这个工厂类可以生成相应的对象。而通过实现FactoryBean接口,我们可以实现自己的工厂类

主要方法

  1. getObject():返回由 FactoryBean 创建的 Bean 实例,如果 isSingleton() 返回 true ,则该实例会放到Spring 容器中单实例缓存池中
  2. isSingleton():如果FactoryBean返回的是单例,该方法返回值为true,否则为false
  3. getObjectType():返回 FactoryBean 创建的 Bean 类型。

上代码

传统的Spring容器加载一个Bean的整个过程,都是由Spring控制的,换句话说,开发者除了设置Bean相关属性之外,是没有太多的自主权的。FactoryBean改变了这一点,开发者可以个性化地定制自己想要实例化出来的Bean,方法就是实现FactoryBean接口。

看一下代码例子,为了讲清楚FactoryBean,内容相对多一些,首先定义一个接口Animal:

代码结构

Animal.java

public interface Animal {
    public void move();
}

定义两个实现类Monkey和Tiger:

Monkey.java

public class Monkey implements Animal {
    public void move() {
        System.out.println("Monkey move!");
    }
}

Tiger.java

public class Tiger implements Animal {
    public void move() {
        System.out.println("Tiger move!");
    }
}

写一个实现类,实现FactoryBean接口:

AnimalFactoryBean.java

public class AnimalFactoryBean implements FactoryBean<Animal> {
    private String animal;

    public Animal getObject() {
        if ("Monkey".equals(animal)) {
            return new Monkey();
        } else if ("Tiger".equals(animal)) {
            return new Tiger();
        } else {
            return null;
        }
    }

    public Class<?> getObjectType() {
        return Animal.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public void setAnimal(String animal) {
        this.animal = animal;
    }
}

配置一个fb2.xml,注入属性Tiger:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
    <bean id="animal" class="org.xrq.bean.factory.AnimalFactoryBean">
        <property name="animal" value="Tiger"/>
    </bean>
</beans>

Main.java

public class Main {
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("fb2.xml");
        Animal animal = (Animal) factory.getBean("animal");
        animal.move();
    }
}

运行结果

关于类型

当配置文件中<bean> 的 class 属性配置的实现类是 FactoryBean 时,通过 getBean() 方法返回的不是FactoryBean 本身,而是 FactoryBean.getObject() 方法所返回的对象,相当于 FactoryBean.getObject() 代理了getBean() 方法。

那么如果就是想获得FactoryBean类型呢,只需要加上”&”符号,具体看下面代码

还是上面的例子,只修改Main.java

public class Main {
    public static void main(String[] args) {
        BeanFactory factory = new ClassPathXmlApplicationContext("fb2.xml");
//        Animal animal = (Animal) factory.getBean("animal");
//        animal.move();
        Object animal = factory.getBean("animal");
        Object animalFactoryBean = factory.getBean("&animal");
        System.out.println("animal类型:" + animal.getClass().getTypeName());
        System.out.println("animalFactoryBean类型:" + animalFactoryBean.getClass().getTypeName());
    }
}

运行结果

小结

  1. 通过转义符&来区分获取FactoryBean产生的对象和FactoryBean对象本身(FactoryBean实现类)。
  2. FactoryBean可以说为IoC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IoC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以在getObject()方法中灵活配置。

BeanFactory、FactoryBean两者区别

  1. BeanFactory是一个IoC基础容器,统一管理所有的Bean。
  2. FactoryBean是一个Bean,但不是一个普通Bean是一个工厂Bean,也就是说它也可能生产bean。但是无论是那种Bean,只要是Bean,都是由IoC容器来管理的。

参考

http://liolin.com/2016/11/13/Spring%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20IoC%20%E5%AE%B9%E5%99%A8%E4%B9%8B%20BeanFactory%20%E5%92%8C%20ApplicationContext/

https://juejin.im/post/5b9f707ce51d450e3f6b8244

https://www.jianshu.com/u/d3f1e12064a5

https://Spring.io/blog/2011/08/09/what-s-a-factorybean

https://my.oschina.net/u/2377110/blog/918659

https://www.cnblogs.com/aspirant/p/9082858.html

https://zhouxiaowu.coding.me/2018/07/19/%E7%90%86%E8%A7%A3Spring%E4%B8%AD%E7%9A%84BeanFactory%E4%B8%8EFactoryBean%E5%8C%BA%E5%88%AB/

https://www.cnblogs.com/xiaoxi/p/5846416.html

http://www.cnblogs.com/xrq730/p/5721366.html

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值