Spring 中 BeanFactory 与 FactoryBean 的区别

Posted 琦彦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 中 BeanFactory 与 FactoryBean 的区别相关的知识,希望对你有一定的参考价值。

Spring 中 BeanFactory 与 FactoryBean 的区别

直接区别

直面意思:Bean工厂、工厂Bean

1、BeanFactory:以 Factory 结尾,表示它是一个工厂类(接口),用于管理 Bean 的一个工厂。在 Spring 中,BeanFactory 是 IOC 容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖

2、FactoryBean:以 Bean 结尾,表示它是一个 Bean,不同于普通 Bean 的是:它是实现了 FactoryBean<T> 接口的 Bean,通过该 Bean 的 ID 从 BeanFactory 中获取的实际上是 FactoryBean 的 getObject() 返回的对象,而不是 FactoryBean 本身,如果要获取 FactoryBean 对象,请在 id 前面加一个 & 符号来获取。

BeanFactory 是什么?

BeanFactory,以 Factory 结尾,表示它是一个工厂类(接口),用于管理 Bean 的一个工厂。在 Spring 中,BeanFactory 是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

BeanFactory 是一个接口,它是 Spring 中工厂的顶层规范,是 SpringIoc 容器的核心接口,它定义了 getBean()containsBean() 等管理 Bean 的通用方法。

Spring 的容器都是它的具体实现如:

  • DefaultListableBeanFactory
  • XmlBeanFactory
  • ApplicationContext

这些实现类又从不同的维度分别有不同的扩展。

例如 XmlBeanFactory 类将持有此 XML 配置元数据,并用它来构建一个完全可配置的系统或应用,如下实例化容器

Resource resource = new FileSystemResource("beans.xml");
BeanFactory factory = new XmlBeanFactory(resource);
ClassPathResource resource = new ClassPathResource("beans.xml");

BeanFactory factory = new XmlBeanFactory(resource);

ApplicationContext context = 
    new ClassPathXmlApplicationContext(new String[] "applicationContext.xml", "applicationContext-part2.xml");

BeanFactory factory = (BeanFactory) context;

BeanFactory 接口解读

下面介绍一下这个核心接口的内容

public interface BeanFactory 

	// 对 FactoryBean 的转义定义
	// 因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象,
	// 如果需要得到工厂本身,需要转义
	String FACTORY_BEAN_PREFIX = "&";

	// 根据 bean 的名字,获取在 IOC 容器中得到 bean 实例
	Object getBean(String name) throws BeansException;

	//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
	<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;

	Object getBean(String name, Object... args) throws BeansException;

	<T> T getBean(Class<T> requiredType) throws BeansException;

	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	//提供对bean的检索,看看是否在IOC容器有这个名字的bean
	boolean containsBean(String name);

	//根据bean名字得到bean实例,并同时判断这个bean是不是单例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到bean实例的Class类型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
	String[] getAliases(String name);

这个 BeanFactory 使用场景

1、从 Ioc 容器中获取 Bean(byName or byType)

2、检索 Ioc 容器中是否包含指定的 Bean

3、判断 Bean 是否为单例

FactoryBean 是什么?

一般情况下,Spring 通过反射机制利用 <bean> 的 class 属性指定实现类实例化 Bean,在某些情况下,实例化 Bean 过程比较复杂,如果按照传统的方式,则需要在 <bean> 中提供大量的配置信息

因为配置方式的灵活性是受限的,这时采用编码的方式反而可能更快,这样就违背了 Spring 的意图了,所以 Spring 为此提供了一个 FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑

第三方框架要继承进 Spring,往往就是通过实现 FactoryBean 来集成的。比如 MyBatis 的 SqlSessionFactoryBean、RedisRepositoryFactoryBean、EhCacheManagerFactoryBean 等等

实现了这个 FactoryBean 接口的类,它只是一个 Bean,它是一个生产或修饰对象生成的工厂 Bean,类似于设计模式中的工厂模式和装饰器模式。

它能生产一个对象,且不仅仅限于它自身,它能返回任何 Bean 的实例。

补充:FactoryBean 接口对于 Spring 框架来说占用重要的地位,Spring自身就提供了70多个 FactoryBean 的实现。

这个接口如下所示:

public interface FactoryBean<T> 

	//从工厂中获取bean
	@Nullable
	T getObject() throws Exception;

	//获取Bean工厂创建的对象的类型
	@Nullable
	Class<?> getObjectType();

	//Bean工厂创建的对象是否是单例模式
	default boolean isSingleton() 
		return true;
	

从 FactoryBean 定义的接口可以看出,FactoryBean 表现的是一个工厂的职责。 即一个 Bean A 如果实现了 FactoryBean 接口,那么 A 就变成了一个工厂,根据 A 的名称获取到的实际上是工厂调用 getObject() 返回的对象,而不是 A 本身,如果要获取工厂 A 自身的实例,那么需要在名称前面加上 '&' 符号。

  • getObject('name') 返回工厂中的实例
  • getObject('&name') 返回工厂本身的实例

注意:通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。

补充:在 Bean 循环依赖三级缓存中可以看到:

/** 三级级缓存:存放 bean 工厂对象,用于解决循环依赖 */
private final Map<String, ObjectFactory<?>> singletonFactories = 
  new HashMap<>(16);

它是做第三级缓存的

FactoryBean 使用场景

说了这么多,为什么要有 FactoryBean 这个东西呢,有什么具体的作用吗?

FactoryBean 在 Spring 中最为典型的一个应用就是用来 创建 AOP 的代理对象

我们知道 AOP 实际上是 Spring 在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。

更形象地说,AOP 代理对象通过 Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在 Spring 中就是——ProxyFactoryBean

所以,FactoryBean 为我们实例化 Bean 提供了一个更为灵活的方式,我们可以通过 FactoryBean 创建出更为复杂的 Bean 实例

使用示例

注意:一般 Spring 内置的 FactoryBean 就足够使用了,这里只是为了演示如何自定义 FactoryBean

先定义一个 Bean 实现 FactoryBean 接口

@Component
public class MyBean implements FactoryBean 
    private String message;
    public MyBean() 
        this.message = "通过构造方法初始化实例";
    

    @Override
    public Object getObject() throws Exception 
        // 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
        //如return new Student()...
        return new MyBean("通过FactoryBean.getObject()创建实例");
    

    @Override
    public Class<?> getObjectType() 
        return MyBean.class;
    

    public String getMessage() 
        return message;
    

MyBean 实现了 FactoryBean 接口的两个方法,getObject() 是可以返回任何对象的实例的,这里测试就返回 MyBean 自身实例,且返回前给 message 字段赋值。同时在构造方法中也为 message 赋值。然后测试代码中先通过名称获取 Bean 实例,打印 message 的内容,再通过 &+名称 获取实例并打印 message 内容。

编写一个测试类,使用这个自定义的 FactoryBean

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class FactoryBeanTest 
    @Autowired
    private ApplicationContext context;

    @Test
    public void test() 
        MyBean myBean1 = (MyBean) context.getBean("myBean");
        System.out.println("myBean1 = " + myBean1.getMessage());

        MyBean myBean2 = (MyBean) context.getBean("&myBean");

        System.out.println("myBean2 = " + myBean2.getMessage());
        System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
    

打印结果

Code
myBean1 = 通过FactoryBean.getObject()初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false

它们两个的区别

BeanFacotry 是 Spring 中比较原始的 Factory。如 XMLBeanFactory 就是一种典型的BeanFactory。

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

原始的 BeanFactory 无法支持 Spring 的许多插件,如 AOP 功能、Web 应用等。

即他们两个都是个工厂,但 FactoryBean 本质上还是一个 Bean,也归 BeanFactory 管理,而 BeanFactory 是 Spring 容器的顶层接口,FactoryBean 更类似于用户自定义的工厂接口。

总结:

BeanFactory 是接口,提供了 IOC 容器最基本的形式,给具体的 IOC 容器的实现提供了规范,

FactoryBean 也是接口,为 IOC 容器中 Bean 的实现提供了更加灵活的方式,FactoryBean在 IOC 容器的基础上给 Bean 的实现加上了一个简单工厂模式和装饰模式,我们可以在 getObject() 方法中灵活配置。

Reference

【小家Spring】一文读懂Spring中的BeanFactory和FactoryBean(以及它和ObjectFactory的区别)的区别

Spring中BeanFactory与FactoryBean的区别

掌握Spring中的beanfactory与factorybean有什么好处?

https://alsritter.icu/posts/beed7112/

以上是关于Spring 中 BeanFactory 与 FactoryBean 的区别的主要内容,如果未能解决你的问题,请参考以下文章

spring中ApplicationContext与BeanFactory容器的区别

Spring中BeanFactory与FactoryBean的区别

Spring中ApplicationContext与BeanFactory容器的区别

Spring BeanFactory与FactoryBean的区别及其各自的详细介绍于用法

Spring从入门到精通—IOC之BeanFactory与ApplicationContext区别

Spring中BeanFactory与FactoryBean的区别