从源码解读深入分析BeanFactory和FactoryBean的区别是什么?

参考链接:[The difference between BeanFactory and FactoryBean in Spring]
https://laptrinhx.com/the-difference-between-beanfactory-and-factorybean-in-spring-3698214575/

一、两者比较

在Spring中,有两个接口BeanFactory FactoryBean ,这两者的名字很相似,但是它们之间的区别是什么呢?

(1)BeanFactory :是IOC容器或对象工厂,并且提供了方法,支持外部程序对这些 bean 的访问。在程序启动时 根据传入的参数产生各种类型的 bean,并添加到 IOC容器(实现 BeanFactory 接口的类) 的 singletonObject 属性中。
(2)FactoryBean:是一个Bean,也存放在 BeanFactory 中,是一个能生产或者修饰对象生成的工厂Bean。它具有工厂方法的功能,在程序运行中 产生指定(一种)类型的 bean,并添加到了 IOC容器中的factoryBeanObjectCache属性中。

所以,这两种方式创建的 bean 都是被 spring 容器管理的。

二、深入分析

2.1 BeanFactory

(1)负责生产和管理Spring中bean的一个工厂;
(2)BeanFactorySpring IOC 容器的核心接口,负责实例化、定位、配置应用程序中的对象及建立这些对象的依赖。它定义了 Spring 管理 bean 的通用方法,比如:getbean() containsBean()
(3)多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
在这里插入图片描述

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);
}

2.2 使用场景

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

2.3 FactoryBean

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

当在IOC容器中的Bean实现了FactoryBean后,通过getBean(String BeanName)获取到的Bean对象并不是FactoryBean的实现类对象,而是这个实现类中的getObject()方法返回的对象。要想获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。

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

在这里插入图片描述

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;
	}   
}

2.4 举个栗子

这是一个简单的 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 去容器中拿,实际上是 容器中 MyFactorybean 的bean 调用了 getObject() 方法,并将结果返回。
第二次使用 &myFactoryBean 去容器中拿,才是真正拿到了 MyFactorybean 的 bean。
& 就是用于区分到底拿谁的的前缀。

两次拿出来的对象当然是不一样的。

2.5 使用场景

Spring 中 FactoryBean 最大的应用场景是用在 AOP 中。我们都知道,AOP 实际上是 Spring 在运行是创建出来的代理对象,这个对象是在运行时才被创建的,而不是在启动时定义的,这与工厂方法模式是一致的。更生动地说,AOP 代理对象通过 Java 反射机制在运行时创建代理对象,并根据业务需求将相应的方法编织到代理对象的目标方法中。Spring 中的ProxyFactoryBean 就是干这事的。

因此,FactoryBean 提供了更灵活的实例化 bean 的方法。通过 FactoryBean 我们可以创建更复杂的 bean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值