BeanFactory和FactoryBean
BeanFactory和FactoryBean长得很相似,也很容易让我们产生误解,特别是对于初学者而言,搞懂他俩关系非常有必要,因为这两个接口,是Spring框架中非常重要的两个接口,也是很多同鞋在面试中经常被问到的问题
BeanFactory(bean工厂)
BeanFactory是Spring IOC容器中最基本的容器接口,负责生产和管理bean,它为其他具体的IOC容器提供了最基本的规范,这当然也是Spring框架提供给我们日常开发最重要的一点,Spring框架通过BeanFactory来管理整个bean对象的生命周期,包括bean对象的实例化、初始化、销毁等等过程,也很好的为我们解决了Bean对象之间的相互依赖关系,提供多种属性注入的配置方式。
在Spring中还有一个非常重要的接口,ApplicationContext,Spring用它管理整个上下文,而通过阅读源码我们发现,在Spring中的ApplicationContext接口的具体实现类里面,有一个成员变量BeanFactory,我们通过ApplicationContext.getBean的方式来获取的bean对象的时候,实际上是把它交给了BeanFactory来间接获取的。
实际上我们可以这么理解,Spring通过BeanFactory来为我们管理Bean对象的生命周期,而Spring的ApplicationContext为我们的容器提供一个可靠的运行环境。
所以Spring的容器启动,可以简单地分为以下几个步骤
- ApplicationContext环境的准备;
- Bean容器的准备;
- 通过配置文件获取bean对象的定义信息;
- 将Bean定义信息注册到Bean容器中,也就是我们说的BeanFactory;
- 由BeanFactory创建bean对象,并管理bean对象(实例化、初始化、依赖注入等);
当然这中间还有许多的操作。
上面我们说过BeanFactory为我们的bean容器提供了管理Bean对象时最基本的规范,包括bean对象的实例化、初始化、属性注入、后置处理、销毁等等操作。在我们通过ApplicationContext.getBean方法获取bean对象的时候,我们发现实际它是交由BeanFactory来获取的。
实际上,为了加快getBean方法获取bean的速度,BeanFactory的实现类里面为我们提供了多种缓存。比如对于我们单例bean对象,在第一次创建成功后,就会将它放入BeanFactory的Map缓存里面,以便于后期再获取的时候,能够保证获取到的是同一对象,也就保证了bean的单例性。
FactoryBean(工厂bean)
FactoryBean接口,顾名思义工厂Bean,只看名字,我们就知道,它是用来描述bean类型,XXXBean,表示这是一个XXX类型的Bean。
所以我们可以猜出两点来
- 它肯定自身也是一个Bean对象
- 根据XXXBean理论得出,它应该是一个具有XXX功能或属性的Bean,这里XXX是Factory,所以它应该是一个具有创建其它Bean功能的一个Bean对象
public interface FactoryBean<T> {
/**
* 根据泛型返回一个此泛型的具体对象
*
* @return
* @throws Exception
*/
T getObject() throws Exception;
/**
* 获取Class类型
* @return
*/
Class<?> getObjectType();
/**
* 是否是单例
* @return
*/
default boolean isSingleton() {
return true;
}
}
根据上面猜出的结论,我们大致可以得出,只要实现了这个接口对象,它本身也应该是一个bean,应该交由Spring容器进行管理
在FactoryBean接口里面提供了3个方法,3个方法的用途请看代码里的注释
测试代码来一波
1.定义一个实现了FactoryBean接口的bean对象,通过注解的方式,name是teacherBeanFactory
@Component(value = "teacherBeanFactory")
public class Teacher implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
return new Student("yan");
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
/**
* 是否是单例,返回true
*
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
2.Student类,没有标注任何注解
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
}
2.通过注解的方式new一个ApplicationContext
public static void main(String[] args) {
//通过注解的方式启动一个Spring上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Teacher.class);
String[] beanDefinitionNames = applicationContext.getBeanFactory().getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("name: "+beanDefinitionName + " bean: " + applicationContext.getBean(beanDefinitionName));
}
Teacher bean = applicationContext.getBean(Teacher.class);
System.out.println(bean);
}
3.打印输出(还有一些spring内部的bean没有写到这里)
name: teacherBeanFactory bean:com.mashibing.ycb.factorybean.Student@1963006a
com.mashibing.ycb.factorybean.Teacher@7fbe847c
我们可以看到,在Spring容器里面肯定存在两个不同的对象:1.Student 2.Teacher
for循环里面通过bean名称(teacherBeanFactory)获取到的bean居然是Student对象,很神奇吧。
正好印证了我们上面的猜测,FactoryBean不是一个简单的bean,它是一个可以创建其它bean的bean。
通过debug,我们发现在我们的BeanFactory的单例缓存(一级缓存)中,存在着Teacher类型的bean,名称就是我们刚刚在Teacher类上定义的名称——@Component(value = "teacherBeanFactory")
而在测试的时候,跟我们预期不一样的是,通过applicationContext.getBean("teacherBeanFactory")的时候,获取得到居然是一个Student的Bean对象。
其实在getBean的时候,有以下流程
可以看到Student对象的bean实际上是存放在factoryBeanObjectCache中的,并不是存放在SingletonMap(一级缓存)里的
结论
BeanFactory:BeanFactory顾名思义,它是一个工厂,一个bean工厂,描述的是一个工厂,而不是一个bean。它是负责生产和管理bean的一个工厂(很大的工厂,里面有很多的bean)。它是IOC容器最底层的接口,提供了对bean管理的基本规范。
FactoryBean:言而之,它是一个bean,一个带有创建功能的bean,描述的是一个bean,工厂只是它的一个修饰符,提供了一个创建bean的一个渠道,只是提供了一个渠道。
我们通俗点表达
机械厂——————生产很多种机械设备(包括数控机床)(BeanFactory)
数控机床——————生产特殊机械设备(FactoryBean)
如果有写得不准确的地方,请读者大胆在评论区指出