BeanFactory与FactoryBean,相信很多刚翻看Spring源码的同学跟我一样很好奇这俩货怎么长得这么像,分别都是干啥用的。BeanFactory是Spring中Bean工厂的顶层接口,也是我们常说的SpringIOC容器,它定下了IOC容器的一些规范和常用方法并管理着Spring中所有的Bean,今天我们不讲它,我们看一下后面那个FactoryBean。

先说下FactoryBean和其作用再开始分析:首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。


首发地址:https://www.guitu18.com/post/2019/04/28/33.html

上面的解释有点抽象,那么我们浏览一遍FactoryBean在Spring中是怎么应用的就好懂了。我们都知道在Spring中我们的Bean都会被Spring的IOC容器所管理,在AbstractApplicationContext中有一个很重要的方法:refresh(),项目启动或重启的时候refresh()会调用getBean()初始化所有的Bean,这个getBean()最终会指向AbstractBeanFactory中的getBean()方法。

在AbstractBeanFactory中,不管是根据名称还是根据类型,getBean()最终都会调用doGetBean(),在doGetBean()方法中一开始就获取了名称beanName和实例sharedInstance,这个方法太长,这里就贴前面两行。

 		String beanName = this.transformedBeanName(name);
Object sharedInstance = this.getSingleton(beanName);

transformedBeanName(name)是为了获取Bean真正的名称,它会去掉name前面的'&',而getSingleton(beanName)是从父类容器singletonObjects中取的这个Bean的实例。在Spring中还有很多这样的容器,比如DefaultListableBeanFactory中的beanDefinitionMap,它就是的IOC容器真正保存Bean的地方,它是一个HashMap。类似的还有FactoryBeanRegistrySupport中的factoryBeanObjectCache等。

回到doGetBean()方法中,拿到sharedInstance后,后面的一大堆操作做了单例、多例等判断,最终会走到this.getObjectForBeanInstance(),关键部分就在这个方法中,进入方法代码。

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName,
@Nullable RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(this.transformedBeanName(name), beanInstance.getClass());
}
}
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
Object object = null;
if (mbd == null) {
object = this.getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
FactoryBean<?> factory = (FactoryBean)beanInstance;
if (mbd == null && this.containsBeanDefinition(beanName)) {
mbd = this.getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = mbd != null && mbd.isSynthetic();
object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
} else {
return beanInstance;
}
}

在上面的代码中有两个判断分别是beanInstance instanceof FactoryBeanBeanFactoryUtils.isFactoryDereference(name),前面判断的是beanInstance是否属于FactoryBean或其子类的实例,后面这个方法判断name是否不为空且以&开头。

    public static boolean isFactoryDereference(@Nullable String name) {
return name != null && name.startsWith("&");
}

如果beanInstance不属于FactoryBean或其子类的实例,或者name是以&开头就直接返回实例对象beanInstance,否则进入到if分支中。在if分支里的各种if .. ==null判断是为了提高性能,咱们只挑关键部分看:object = this.getObjectFromFactoryBean(factory, beanName, !synthetic); 继续跟踪进去看代码。

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && this.containsSingleton(beanName)) {
synchronized(this.getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = this.doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
} else {
if (shouldPostProcess) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
return object;
}
this.beforeSingletonCreation(beanName);
try {
object = this.postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable var14) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", var14);
} finally {
this.afterSingletonCreation(beanName);
}
}
if (this.containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
Object object = this.doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = this.postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable var17) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", var17);
}
}
return object;
}
}

这里面也是做了很多判断,咱们也只挑关键部分看。这里说一下个人想法,看源码的时候如果我们一直追究所有的细节那会让我们会越陷越深,掉入细节的无底洞,稍不留神脑回路跟不上就会蒙圈。我们要学会找源码中的关键部分看,弄懂主要流程和本次看源码的目的的那部分就行。等我们对Spring整体有了一个很好的理解之后,再回头看之前不懂的代码就会豁然开朗。在上面这个方法中不管是走上面的if分支还是到下边的else中,关键部分就是object = this.doGetObjectFromFactoryBean(factory, beanName)这段代码调用,继续点进去。

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = this.getAccessControlContext();
try {
object = AccessController.doPrivileged(factory::getObject, acc);
} catch (PrivilegedActionException var6) {
throw var6.getException();
}
} else {
object = factory.getObject();
}
} catch (FactoryBeanNotInitializedException var7) {
throw new BeanCurrentlyInCreationException(beanName, var7.toString());
} catch (Throwable var8) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var8);
}
if (object == null) {
if (this.isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName,
"FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}

在这个方法中有一行关键代码:object = factory.getObject(); 这个factory就是我们传入的beanInstance实例。绕了这么一大圈,getBean方法返回的居然是我们实现FactoryBean接口定义的getObject方法。

那么跟一开始对FactoryBean的描述印证了,FactoryBean是一个能生产或修饰对象生成的工厂Bean。一个Bean如果实现了FactoryBean接口,那么根据该Bean的名称获取到的实际上是getObject()返回的对象,而不是这个Bean自身实例,如果要获取这个Bean自身实例,那么需要在名称前面加上'&'符号

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式


原理弄明白了,下面通过代码测试验证上面的流程,先定义一个Bean实现FactoryBean接口。

@Component
public class MyBean implements FactoryBean {
private String message;
public MyBean() {
this.message = "通过构造方法初始化实例";
}
@Override
public Object getObject() throws Exception {
MyBean myBean = new MyBean();
myBean.message = "通过FactoryBean.getObject()创建实例";
// 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例
return myBean;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
public String getMessage() {
return message;
}
}

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

@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));
}
}
myBean1 = 通过FactoryBean.getObject()初始化实例
myBean2 = 通过构造方法初始化实例
myBean1.equals(myBean2) = false

通过测试我们发现获取的两个实例中的message的值不一样,对比两个对象的引用也不相同。上述所讲关于FactoryBean的内容印证完毕,本文结束。

Spring中FactoryBean的作用和实现原理的更多相关文章

  1. Spring 中bean的作用、定义

    Spring 中bean的作用.定义: 创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的"配方(recipe)".把bean定义看成一个配方很有意义,它与cl ...

  2. Spring 中的反射与反射的原理

    作者:DeppWang.原文地址 在造轮子:实现一个简易的 Spring IoC 容器一文中提到 Spring 在创建 Bean 实例和依赖注入时使用了反射,本文来具体分析一下 Spring 中的反射 ...

  3. spring中的Log4jConfigListener作用和webapp.root的设置

    转:http://blog.sina.com.cn/s/blog_7bbf356c01016wld.html 使用spring中的Log4jConfigListener有如如下好处:     1. 动 ...

  4. @Transacitonal注解不生效之spring中expose-proxy的作用与原理

    几年前记得整理过,@Transacitonal注解的方法被另外一个方法调用的时候,事务是不生效的. 如果大量代码已经这么写了,这个时候抽取出去不现实,怎么办呢? 答案就是在<aop:aspect ...

  5. Spring中FactoryBean与BeanFactory的区别

    版本:spring-framework-4.1 一概述 BeanFactory 与 FactoryBean的区别, 两个名字很像,面试中也经常遇到,所以容易搞混,现从源码以及示例两方面来分析. 二.源 ...

  6. Spring中的TransactionProxyFactoryBean作用及配置(转)

    问: 原文链接 http://blog.csdn.net/cpp_lzth/article/details/6551703 看AOP的时候发现spring中有个org.springframework. ...

  7. Spring中ApplicationContextAware的作用

    ApplicationContextAware 通过它Spring容器会自动把上下文环境对象调用ApplicationContextAware接口中的setApplicationContext方法. ...

  8. Spring中depends-on的作用是什么?

    spring的IOC容器负责bean的管理,当实例化一个bean是,spring保证该Bean所依赖的其他bean已经初始化.一般情况下,用<ref>元素建立对其他bean的依赖关系. 比 ...

  9. Spring 中context.start作用

    我们经常会看到 如下代码 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(configPath. ...

随机推荐

  1. [apue] 等待子进程的那些事儿

    谈到等待子进程,首先想到的就是SIGCHLD信号与wait函数族,本文试图厘清二者的方方面面,以及组合使用时可能不小心掉进去的坑. 1. 首先谈单独使用SIGCHLD的场景.下面是一段典型的代码片段: ...

  2. Thinkphp5.0之异常处理

    1.默认异常处理在调试模式下,系统默认展示的错误页面:请输入图片描述 异常处理接管 1.修改config.php 'app_debug' => false,2.在配置文件里添加如下代码 // 异 ...

  3. Presto 0.22.0 安装记录

    1. 下载 & 解压 # 下载 wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.220/pres ...

  4. SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面

    前言 SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现.为Java Util Logging.Log4J2和Logback提供了默认配置.在不同的情况下,日志记录器都预先配置为使 ...

  5. JDK源码阅读(三):ArraryList源码解析

    今天来看一下ArrayList的源码 目录 介绍 继承结构 属性 构造方法 add方法 remove方法 修改方法 获取元素 size()方法 isEmpty方法 clear方法 循环数组 1.介绍 ...

  6. Keep It Simple

    The KISS principle, or Keep It Simple, Stupid, spans many trades, industries, and professions. The m ...

  7. Web自动化测试 三 ----- DOM对象和元素查找

    一.DOM对象 DOM(Document Object Model文档对象模型):将HTML的各种元素映射为JS可访问的对象.HTML文档中的所有内容都是节点,这些东西在HTML中我们称为元素. 整个 ...

  8. k8s学习 - 概念 - master/node

    k8s学习 - 概念 - master/node 在k8s中,有各种各样的概念和术语.这些概念是必须要学习和掌握的.我们先罗列下所有概念,然后再一个个看具体实例. 大概说一下这些概念: Master: ...

  9. GPS常识-B版(简)

    第一章 绪论 1.简述GPS系统的特点有哪些? 在测绘工程中有如下优点:(1)定位精度高(2)观测时间短(3)测站间无需通视(4)可提供地心坐标(5)操作简便(6)全天候作业(7)功能多.应用广 GP ...

  10. python之pip install

    安装方式1 wget  http://python-distribute.org/distribute_setup.py sudo python distribute_setup.py wget  h ...