在上篇文章中我已经对容器的第一个扩展点(BeanFactoryPostProcessor)做了一系列的介绍。其中主要介绍了 Spring 容器中 BeanFactoryPostProcessor 的执行流程,以及 Spring 自身利用了 BeanFactoryPostProcessor 完成了什么功能,对于一些细节问题可能说的不够仔细,但是当前阶段我想要做的主要是为了以后学习源码打下基础,所以对于这些问题我暂且不去过多纠结,待到源码学习会进行更加细致的分析。在本篇文章中,我们将要学习的是容器的另一个扩展点(FactoryBean),对于 FactoryBean 官网的介绍很短,但是如果我们对 Spring 的源码有一定了解可以发现,Spring 在很多地方都对这个特殊的 Bean 做了处理。。。
先看看官网上是怎么说的:
从上面这段文字我们可以得出以下几个信息:
- FactoryBean 主要用来定制化 Bean 的创建逻辑。
- 当我们实例化一个 Bean 的逻辑很复杂的时候,使用 FactoryBean 是很必要的,这样可以规避我们去使用冗长的 XML 配置。
- FactoryBean 接口提供了三个方法:
- Object getObject():返回这个 FactoryBean 所创建的对象。
- boolean isSingleton():返回 FactoryBean 所创建的对象是否为单例,默认返回 true。
- Class getObjectType():返回这个 FactoryBean 所创建的对象的类型,如果我们能确认返回对象的类型的话,我们应该正常对这个方法做出实现,而不是返回 null。
- Spring 自身大量使用了 FactoryBean 这个概念,至少有 50 个 FactoryBean 的实现类存在于 Spring 容器中
- 假设我们定义了一个 FactoryBean,名为 MyFactoryBean,当我们调用 getBean("MyFactoryBean")方法时返回的并不是这个 FactoryBean,而是这个 FactoryBean 所创建的 Bean,如果我们想获取到这个 FactoryBean 需要在名字前面拼接"&",例如这种形式:getBean("&MyFactoryBean")
上面这些概念可能刚刚说的时候大家不是很明白,下面我们通过 FactoryBean 的一些应用来进一步体会这个接口的作用。
FactoryBean 的应用
我们先看下面的 Demo:
@Component
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
System.out.println("执行了一段复杂的创建 Bean 的逻辑");
return new TestBean();
}
@Override
public Class<?> getObjectType() {
return TestBean.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration
@ComponentScan("com.wxx.service")
public class Appconfig {
}
public class TestBean {
public TestBean(){
System.out.println("TestBean 被创建出来了");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac=
new AnnotationConfigApplicationContext(Appconfig.class);
System.out.println("直接调用 getBean(\"myFactoryBean\")返回:"+ac.getBean("myFactoryBean"));
System.out.println("调用 getBean(\"&myFactoryBean\")返回:"+ac.getBean("&myFactoryBean"));
}
}
运行结果如下:
执行了一段复杂的创建 Bean 的逻辑
TestBean 被创建出来了
直接调用 getBean("myFactoryBean")返回:com.dmz.official.extension.factorybean.TestBean@28f67ac7
调用 getBean("&myFactoryBean")返回:com.dmz.official.extension.factorybean.MyFactoryBean@256216b3
虽然没有直接将 TestBean 放入 Spring 容器中,但是通过 FactoryBean 也完成了这一操作。同时当我们直接调用 getBean("myFactoryBean")获取到的是 FactoryBean 创建的 Bean,但是添加了"&"后获取到的是 FactoryB 本身。
FactoryBean 相关源码分析
我们先看下面这图:
涉及到 FactoryBean 主要在 3-11-6 这一步,我们主要关注下面这段代码:
我们按照顺序一步一步分析,首先看第一步:
- 判断是不是一个 FactoryBean,对应源码如下:
- 如果是一个 FactoryBean,那么在 getBean 的时候,添加前缀"&",获取这个 FactoryBean
- 判断是不是一个 SmartFactoryBean,并且不是懒加载
这里涉及到一个概念,就是 SmartFactoryBean,实际上这个接口继承了 FactoryBean 接口,并且 SmartFactoryBean 是 FactoryBean 的唯一子接口,它扩展了 FactoryBean 多提供了两个方法:
从上面的代码中可以看出,当我们实现了一个 FactoryBean 接口,Spring 并不会在启动时就将这个 FactoryBean 所创建的 bean 创建出来,为了避免这种情况,有两种方法:
- 实现 SmartFactoryBean,并重写 isEagerInit 方法,将返回值设置为 true。
- 我们也可以在一个不是懒加载的 bean 中注入这个 FactoryBean 所创建的 bean,Spring 在解决依赖关系也会帮我们将这个 bean 创建出来。
实际上我们可以发现,当我们仅仅实现 FactoryBean 时,其 getObject()方法所产生的 Bean,我们可以当是懒加载的。
- 如果是一个 SmartFactoryBean 并且不是懒加载,那么创建这个 FactoryBean 创建的 bean。这里需要注意的是此时创建的不是这个 FactoryBean,以为在 getBean 时并没有加一个前缀"&",所以获取到的是其 getObject()方法所产生的 bean。
在上面的代码分析完后,在 3-6-11-2 中也有两行 FactoryBean 相关的代码:
- 获取 bean 名称
- 如果是一个 FactoryBean,将会调用其 getObject 方法,如果不是直接返回。
我们可以看到,在调用 getOBjectForInstance(sharedInstance, name, beanName, null);传入了一个参数 name,也就是还没有经过 transformedBeanName 方法处理的 bean 的名称,可能会带有"&"符号,SPring 通过这个参数判断这个 bean 是不是一个 FactoryBean,如果是的话会调用 getObject 创建 bean,被创建的 bean 不会存放在单例池中,而是放在一个名为 factoryBeanObjectCache 的缓存中。具体的代码比较复杂,在这里就不分析了,先留个印象。
Spring 中"FactoryBean"概念的汇总(纯个人观点)
除了我们在上文说到的实现了 FactoryBean 或者 SmartFactoryBean 接口的 bean 可被称为一个 FactoryBean,不知道对 BeanDefinition 中的一个属性是否还有印象。实际上这个属性存在于 AbstractBeanDefinition 中:
对于这个属性和我们这篇文章中介绍的 FactoryBean 有什么关系呢?
首先看看什么情况下 BeanDefinition 会存在这个属性,主要分为两种情况:
- 第一种情况:
@Configuration
public class Config {
@Bean
public B b(){
return new B();
}
}
我们通过@Bean 方式创建一个 Bean,那么在 B 的 BeanDefinition 会记录 factoryBeanName 这个属性,同时还会记录是这个 bean 中的哪个方法创建 B 的,在上面的例子中:factoryBeanName=config,factoryMethodName=b。
- 第二种情况:
<bean id="factoryBean" class="com.dmz.official.extension.factorybean.C"/>
<bean id="b" class="com.dmz.official.extension.factorybean.B" factory-bean="factoryBean" factory-method="b"/>
通过 XML 的方式进行配置,此时 B 的 BeanDefinition 中 factoryBeanName=factoryBean,factoryMethodName=b。
上面两种情况,BeanDefinition 中的 factoryBeanName 这个属性不会为空,但是请注意此时记录的这个名字对 bean 并不是一个实现了 FactoryBean 接口的 bean。
综上,我们可以将 Spring 中的 FactoryBean 的概念泛华,也就是说所有产生对象的 bean 我们都将其称为 FactoryBean,可以总结画图如下:
和 FactoryBean 相关的面试题
1、FactoryBean 和 BeanFactory 的区别
factoryBean 就如我们标题所说,是 Spring 提供的一个扩展点,适用于复杂的 Bean 的创建。mybatis 和 Spring 整合的时候就用到了这个扩展点,并且 FactoryBean 所创建的 bean 和普通的 bean 不一样,可以说 FactoryBean 是 Spring 创建 bean 的另外一种手段。
而 BeanFactory 是什么呢?BeanFac 是 Spring IOC 容器的顶级接口,其实现类有 XMLBeanFac,DefaultListableBeanFactory 以及 AnnotationConfigApplicationContext 等。BeanFactory 为 Spring 管理 Bean 提供了一套通用的规范,接口中提供的一些方法如下:
通过这些方法,可以方便的获取 bean,对 bean 进行操作和判断。
- 如何把一个对象交给 Spring 管理
首先要明白一点,怎么把一个对象交给 Spring 管理,"对象"要划重点,我们通常采用注解如@Compent 或者 XML 配置这种类似操作并不能将一个对象交给 Spring 管理,而是让 Spring 根据我们的配置信息以及信息创建并管理了这个对象,形成了 Spring 中的一个 bean,把一个对象交给 Spring 管理主要有两种方式:
- 就是用我们这篇文章的主角,FactoryBean,我们直接在 FactoryBean 的 getObject 方法直接返回需要被管理的对象即可
- @Bean 注解,同一通过@Bean 注解标志的方法直接返回需要被管理的对象即可。
总结
在本文中将了 FactoryBean 的学习,最重要的一点是,FactoryBean 是 Spring 中特殊的一个 bean,Spring 利用它提供了另一种创建 Bean 的方式,FactoryBean 整体的体系比较复杂,FactoryBean 是如何创建一个 Bean 的一些细节还没有涉及到,不过接下来在源码还会接触到它,并会对其整改流程做进一步的分析。目前容器的扩展点还剩最后一个部分,即 BeanPostProcessor。BeanPostProcessor 贯穿了整改 Spring 的生命周期,学习难度更大。
最后,认认真真学习,加油,共勉!!!
标签:Spring,Bean,bean,源码,创建,FactoryBean,public From: https://blog.51cto.com/u_15668812/7435669