BeanFactory和FactoryBean的区别

1. 前言

在getBean方法中,即 bean 实例化完成之后,存在这样一段代码:

image-20211108153552672

从方法名上可以得知它是用来通过实例创建对象的,既然上面已经创建了,这里为什么还会在创建一次呢?这就和FactoryBean有关。

2. BeanFactory和FactoryBean

从定义上看,一个是bean工厂,一个是工厂bean,在 Spring 中,它们都是一个接口,都可以用来创建Bean实例,但各自的功能点却又存在不同。

2.1 BeanFactory

BeanFactory,他是Spring容器的一个顶层接口,里面定义了Spring IOC容器的一些规范,可以把它理解为一个容器。它最主要的方法就是getBean(),该方法从容器中返回特定名称的Bean,BeanFactory 的功能通过其他的接口得到不断扩展。比如最常用的 ApplicationContext,也是其子类。

这个接口其实可以理解为 Spring 中用来创建和管理 Bean 实例的另外一个IOC容器,这个接口中除了包括BeanFactory 中的所有规范,还提供了其他的一些扩展功能,他的常用实现包括如下几个:

  1. FileSystemXmlApplicationContext:通过加载一个文件系统中的xml来初始化一个Spring容器;
  2. AnnotationConfigApplicationContext:通过加载一个JavaConfig配置类来初始化Spring容器;
  3. ClassPathXmlApplicationContext:通过加载classpath下的xml配置文件来初始化Spring容器。

如果使用 BeanFactory 的实现类来创建 Bean,那么就必须要遵守 Spring 中 Bean 的实例化流程

2.2 FactoryBean

FactoryBean,实际上就是一个Java Bean,但是它是一个能生产对象的工厂Bean,BeanFactory创建对象的过程是非常复杂的,至少 xml 的方式肯定会让你头疼。

因此,Spring 提供了一个 FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 Bean 的逻辑。

3. 简单使用

FactoryBean是一个接口,如果要使用FactoryBean,需要自定义一个FactoryBean的实现类,如下:

@Component
public class MyFactoryBean implements FactoryBean<Student> {

    @Override
    public Student getObject() throws Exception {
        // 该方法内可以编写较为复杂的创建逻辑...
        Student student = new Student();
        student.setName("factoryBean");
        student.setAge(18);
        return student;
    }

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

然后创建一个Student类:

import lombok.Data;

@Data
public class Student {

    private String name;

    private int age;
}

此时存在一个问题,我们的目的是想生成 Student 实例,但 MyFactoryBean 肯定会由 BeanFactory 来创建,而Student 通过FactoryBean 来创建,那么怎么区分呢?

Spring 使用了 “&” 前缀。如 beanName 为 myFactoryBean ,则 getBean("myFactoryBean") 获得的是 MyFactoryBean通过 getObject() 方法创建的 bean 实例,而 getBean("&myFactoryBean") 获得的是 MyFactoryBean 本身。

测试:

@Test
public void test5() {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Student student = applicationContext.getBean("myFactoryBean", Student.class);
    System.out.println("myFactoryBean--->" + student);
    MyFactoryBean factoryBean = applicationContext.getBean("&myFactoryBean", MyFactoryBean.class);
    System.out.println("&myFactoryBean--->" + factoryBean);
}

输出:

myFactoryBean--->Student(name=factoryBean, age=18)
&myFactoryBean--->net.javatv.factorybean.MyFactoryBean@50eac852

4. 源码分析

对于 FactoryBean 的源码相对来说非常简单,进入AbstractBeanFactory#getObjectForBeanInstance()

1、首先会针对 beanName 进行判断,如果要获取到 FactoryBean 类本身,就必须加上”&”符号。

image-20211108162914117

2、尝试从缓存中拿取,同 BeanFactory,它也有自己的缓存,但跟其他实例不是同一个缓存,对应的缓存是:factoryBeanObjectCache

image-20211108163415895

3、如果缓存中没有,则进入 getObjectFromFactoryBean获取对象。

image-20211108163700710

进入该方法,其中包括了单例和多例的情况。

image-20211108163850082

找到真正做事的方法doGetObjectFromFactoryBean,可以看到就是调用的 FactoryBean 的 getObject()方法。

image-20211108164040044

而此时getObject()的实现类就是 MyFactoryBean 的实现:

image-20211108164156878

5. 总结

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

我们知道 AOP 实际上是 Spring 在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP 代理对象通过 Java 的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是ProxyFactoryBean

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

另外,Mybatis 中也使用到了 FactoryBean,在获取 Mapper 对象的时候,在getMapper方法底层就会通过JDK的动态代理去生成Mapper的默认实现,所以我们 Mapper 一般不需要写实现类,方法也可以直接调用。

image-20211108172733140

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

汪了个王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值