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,这个方法太长,这里就贴前面两行。

  1. String beanName = this.transformedBeanName(name);
  2. 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(),关键部分就在这个方法中,进入方法代码。

  1. protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName,
  2. @Nullable RootBeanDefinition mbd) {
  3. if (BeanFactoryUtils.isFactoryDereference(name)) {
  4. if (beanInstance instanceof NullBean) {
  5. return beanInstance;
  6. }
  7. if (!(beanInstance instanceof FactoryBean)) {
  8. throw new BeanIsNotAFactoryException(this.transformedBeanName(name), beanInstance.getClass());
  9. }
  10. }
  11. if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
  12. Object object = null;
  13. if (mbd == null) {
  14. object = this.getCachedObjectForFactoryBean(beanName);
  15. }
  16. if (object == null) {
  17. FactoryBean<?> factory = (FactoryBean)beanInstance;
  18. if (mbd == null && this.containsBeanDefinition(beanName)) {
  19. mbd = this.getMergedLocalBeanDefinition(beanName);
  20. }
  21. boolean synthetic = mbd != null && mbd.isSynthetic();
  22. object = this.getObjectFromFactoryBean(factory, beanName, !synthetic);
  23. }
  24. return object;
  25. } else {
  26. return beanInstance;
  27. }
  28. }

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

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

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

  1. protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
  2. if (factory.isSingleton() && this.containsSingleton(beanName)) {
  3. synchronized(this.getSingletonMutex()) {
  4. Object object = this.factoryBeanObjectCache.get(beanName);
  5. if (object == null) {
  6. object = this.doGetObjectFromFactoryBean(factory, beanName);
  7. Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
  8. if (alreadyThere != null) {
  9. object = alreadyThere;
  10. } else {
  11. if (shouldPostProcess) {
  12. if (this.isSingletonCurrentlyInCreation(beanName)) {
  13. return object;
  14. }
  15. this.beforeSingletonCreation(beanName);
  16. try {
  17. object = this.postProcessObjectFromFactoryBean(object, beanName);
  18. } catch (Throwable var14) {
  19. throw new BeanCreationException(beanName,
  20. "Post-processing of FactoryBean's singleton object failed", var14);
  21. } finally {
  22. this.afterSingletonCreation(beanName);
  23. }
  24. }
  25. if (this.containsSingleton(beanName)) {
  26. this.factoryBeanObjectCache.put(beanName, object);
  27. }
  28. }
  29. }
  30. return object;
  31. }
  32. } else {
  33. Object object = this.doGetObjectFromFactoryBean(factory, beanName);
  34. if (shouldPostProcess) {
  35. try {
  36. object = this.postProcessObjectFromFactoryBean(object, beanName);
  37. } catch (Throwable var17) {
  38. throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", var17);
  39. }
  40. }
  41. return object;
  42. }
  43. }

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

  1. private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
  2. Object object;
  3. try {
  4. if (System.getSecurityManager() != null) {
  5. AccessControlContext acc = this.getAccessControlContext();
  6. try {
  7. object = AccessController.doPrivileged(factory::getObject, acc);
  8. } catch (PrivilegedActionException var6) {
  9. throw var6.getException();
  10. }
  11. } else {
  12. object = factory.getObject();
  13. }
  14. } catch (FactoryBeanNotInitializedException var7) {
  15. throw new BeanCurrentlyInCreationException(beanName, var7.toString());
  16. } catch (Throwable var8) {
  17. throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", var8);
  18. }
  19. if (object == null) {
  20. if (this.isSingletonCurrentlyInCreation(beanName)) {
  21. throw new BeanCurrentlyInCreationException(beanName,
  22. "FactoryBean which is currently in creation returned null from getObject");
  23. }
  24. object = new NullBean();
  25. }
  26. return object;
  27. }

在这个方法中有一行关键代码: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接口。

  1. @Component
  2. public class MyBean implements FactoryBean {
  3. private String message;
  4. public MyBean() {
  5. this.message = "通过构造方法初始化实例";
  6. }
  7. @Override
  8. public Object getObject() throws Exception {
  9. MyBean myBean = new MyBean();
  10. myBean.message = "通过FactoryBean.getObject()创建实例";
  11. // 这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例
  12. return myBean;
  13. }
  14. @Override
  15. public Class<?> getObjectType() {
  16. return MyBean.class;
  17. }
  18. public String getMessage() {
  19. return message;
  20. }
  21. }

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

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest(classes = TestApplication.class)
  3. public class FactoryBeanTest {
  4. @Autowired
  5. private ApplicationContext context;
  6. @Test
  7. public void test() {
  8. MyBean myBean1 = (MyBean) context.getBean("myBean");
  9. System.out.println("myBean1 = " + myBean1.getMessage());
  10. MyBean myBean2 = (MyBean) context.getBean("&myBean");
  11. System.out.println("myBean2 = " + myBean2.getMessage());
  12. System.out.println("myBean1.equals(myBean2) = " + myBean1.equals(myBean2));
  13. }
  14. }
  1. myBean1 = 通过FactoryBean.getObject()初始化实例
  2. myBean2 = 通过构造方法初始化实例
  3. 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. Hadoop 学习之路(二)—— 集群资源管理器 YARN

    一.hadoop yarn 简介 Apache YARN (Yet Another Resource Negotiator) 是hadoop 2.0 引入的集群资源管理系统.用户可以将各种服务框架部署 ...

  2. 【python】UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

    header中干掉 "Accept-Encoding": "gzip, deflate, br", 注意:

  3. maven的下载与安装,卸载替换eclipse自带的maven

    首先呢,博主在这里给大家一个建议,最好不要用eclipse自带的maven.因为这家伙总会出现一些这样那样的错误,比如常见的jar包下载不全或者是install打包报错等等. 博主用了一段时间,还是觉 ...

  4. 数读 | 为什么运维朋友们都需要学Python?

    运维人员需不需要学开发?需不需要学Python?PythonN和Shell有什么区别?天天问这种好水的问题,我实在受不了,决定帮大家扫扫盲. 现阶段,掌握一门开发语言已经成为高级运维工程师的必备计能, ...

  5. 生产追溯系统-Raspberry Pi帮助我们节省大量硬件成本,助力信息化建设

    初识 Raspberry Pi 竟然有这么小的电脑主机?只有手掌这么大?电源线竟然跟手机数据线一样?当我第一次看到Raspberry Pi的时候,在脑海中产生了一连串的疑问,带着这些疑问逐渐开始研究这 ...

  6. JS高级程序设计第2章--精简版

    前言:这次是二刷了,想暑假做一次完整的笔记,但用本子来写笔记的话太贵了,可能哪天还丢了..所以还是博客好== 第二章:在HTML中使用JavaScript 2.1 <script>元素: ...

  7. js常用设计模式实现(一)单例模式

    前言 什么是设计模式 设计模式是一种能够被反复使用,符合面向对象特性的代码设计经验的总结,合理的使用设计模式能够让你得代码更容易维护和可靠 设计模式的类型共分为创建型模式,结构型模式,行为型模式三种 ...

  8. MYSQL5.7---ONLY_FULL_GROUP_BY 异常处理

    异常介绍: ONLY_FULL_GROUP_BY 指的是你查询的语句使用到了group by 例如  select name,age from person group by sex; 此时你grou ...

  9. JavaScript控制语句结构、函数部分

    HTML页面代码: <html> <head> <meta charset="UTF-8"> <title>HelloWorld&l ...

  10. java调用新浪接口根据Ip查询所属地区

    import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import ...