文章目录

  • FactoryBean介绍
  • FactoryBean的使用
    • 使用示例
    • 配置
    • 运行结果
    • 获取FactoryBean自身工厂对象
  • FactoryBean的好处
  • FactoryBean与BeanFactory的区别
  • 源码分析
    • FactoryBean的缓存区
    • 执行流程
    • 源码解析
      • FactoryBean的初始化过程
      • 获取FactoryBean实际生产对象过程
  • 总结

FactoryBean介绍

FactoryBean是Spring提供的一个可以由用户自定义的Bean实例工厂接口,FactoryBean的对象本身由Spring容器管理,但FactoryBean仅作为对象工厂暴露,其生产出的Bean的过程由用户控制。

FactoryBean接口提供了如下方法:

方法 描述
T getObject() 获取该FactoryBean所生产的实际对象, BeanFactory在获取FactoryBean生产的实际对象时,会根据其isSingleton()方法来判定是否创建的对象属于singleton或是prototype
Class<?> getObjectType(); 返回工厂生产的实际对象类型。
boolean isSingleton(); 所生产的对象是否是单例;此方法告知beanFactory,每次获取该工厂的bean对象时是每次调用getObject()来获取新的对象,还是始终获取唯一的单例对象。

FactoryBean的使用

使用示例

package com.baiyang.factorybean;import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.io.Serializable;class Book implements Serializable {private static final long serialVersionUID = -672687543464668808L;private String name;private String author;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author = author;}
}
public class BookFactory implements FactoryBean<Book> {public static boolean isSingleton = true;@Overridepublic Book getObject() throws Exception {Book book = new Book();book.setName("《非暴力沟通》");book.setAuthor("马歇尔·卢森堡");return book;}@Overridepublic Class<?> getObjectType() {return Book.class;}@Overridepublic boolean isSingleton() {return isSingleton;}public static void main(String[] args) {System.out.println("-------工厂singleton模式------");ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");Book book1 = (Book) context.getBean("book");System.out.println("book1:" + book1);Book book2 = (Book) context.getBean("book");System.out.println("book2:" + book2);System.out.println("book1 == book2:" + (book1 == book2));System.out.println("-------工厂prototype模式------");BookFactory.isSingleton = false;context = new ClassPathXmlApplicationContext("factorybean.xml");book1 = (Book) context.getBean("book");System.out.println("book1:" + book1);book2 = (Book) context.getBean("book");System.out.println("book2:" + book2);System.out.println("book1 == book2:" + (book1 == book2));}
}

配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--定义bean工厂--><bean id="book" class="com.baiyang.factorybean.BookFactory"/></beans>

运行结果

-------工厂singleton模式------
book1:com.baiyang.factorybean.Book@5bcab519
book2:com.baiyang.factorybean.Book@5bcab519
book1 == book2:true
-------工厂prototype模式------
book1:com.baiyang.factorybean.Book@73846619
book2:com.baiyang.factorybean.Book@4bec1f0c
book1 == book2:false

可以从结果中看出:

  • 尽管在xml中配置的id为"book"的对象是BookFactory类型,但通过getBean("book")获取的类型是Book类型,非BookFactory类型
  • FactoryBean#isSingleton()返回true时多次获取的book对象都是同一个对象。
  • FactoryBean#isSingleton()返回false时多次获取的book对象都是不同的对象。

获取FactoryBean自身工厂对象

如果需要获取FactoryBean自身的工厂对象需要在BeanId前加上"&“符号;”&“符号可以理解为C语言中获取变量的地址;在此处可以理解为"beanName"如果是工厂bean,则获取的是对应工厂生产的Bean,而”&beanName"获取的是生产该"beanName"的工厂;

System.out.println("--------获取FactoryBean自身对象-------");
System.out.println("BookFactory:" + context.getBean("&book"));

FactoryBean的好处

FactoryBean与BeanFactory的区别

两者都是用来创建对象的,当使用BeanFactory对象的时候必须遵循完整的创建过程,这个过程是由Spring容器来管理控制的。而FactoryBean只需要调用getObject方法就可以返回对象了,整个对象的创建过程是由用户来控制的。

源码分析

FactoryBean的缓存区


FactoryBeanRegistrySupport类实现了对FactoryBean的支持;我们熟知的最常用的Bean工厂DefaultListableBeanFactory继承了FactoryBeanRegistrySupport,从而也获得了FactoryBean的支持。

执行流程

源码解析

FactoryBean的初始化过程

首先看下BeanFactory实例化所有BeanDefinition的过程,来了解下FactoryBean的初始化过程。
先定位到AbstractApplicationCountext类的refresh()方法的finishBeanFactoryInitialization(beanFactory);位置如下:
可以看到当前BeanDefinitionMap中注册了nameBookBeanDefinition,其beanClass指向的FactoryBean实现类BookFactory

refresh()调用finishBeanFactoryInitialization(beanFactory);的最后会调用DefaultListableBeanFactory#preInstantiateSingletons()方法来对BeanFactory中注册的所有BeanDefinition进行初始化:

如上图可以看到最外层循环所有注册的BeanDefinition的代码有区别BeanName是否是FactoryBean来走不同的逻辑。

看下isFactoryBean()的方法是如何判断其为FactoryBean的:
org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String)

org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition)

然后是对FactoryBean的实例化关键点,由于前面对FactoryBeanname加了"&“前缀,那么注册到BeanFactory中的单例Beankey是否也是加了”&"符号的呢?
继续看下:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

上面的第一步transformedBeanName(name)方法会将传入的name转成常规name,即:如果有"&“前缀则将”&"前缀去除。
然后对去除了&的name进行初始化,因为只有这样才能通过转成了常规的name找到BeanDefinition

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

经过以上过程,最终会将FactoryBean对象注册到BeanFactorysingletonObjects属性中,并且name为设置的id值,并不带&符号;

获取FactoryBean实际生产对象过程

那么问题来了,由于singletonObjects里面注册的BeanFactoryBean对象,并不是实际的bean,那么Spring是怎么调用的是FactoryBeangetObject方法返回的呢?
换句话说,Spring用FactoryBean生产出实际bean对象的过程是怎么样的呢?

我们从getBean方法开始:

一路Debug到下面doGetBean

由于FactoryBean在容器初始化时就已经实例化注册到了BeanFactory中,所以Object sharedInstance = getSingleton(beanName);这行代码是可以找到FactoryBean对象的。

关键在上图的bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);这行代码的调用。
由于是获取FactoryBean的实际Bean,所以是没有BeanDefinition的,所以传入的实参是null。
进入看下实现:

由于传入的name不是以&符号开头,所以获取的是FactoryBean生产的对象,首先是从factoryBeanObjectCache缓存中通过beanName获取,如果获取到则直接返回,如果获取不到,则调用object = getObjectFromFactoryBean(factory, beanName, !synthetic);这行代码获取生产的实际对象。

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean

从上图的过程可以看到factoryisSingleton返回的是true的话,那么会首先从factoryBeanObjectCache中获取,如果缓存中获取不到则调用doGetObjectFromFactoryBean方法来实际调用FactoryBeangetObject来获取对象,并将对象放入到factoryBeanObjectCache中用于后续的直接获取。
如果是非单例的话,则直接调用FactoryBeangetObject来每次都新创建对象返回。

返回实际生产对象:
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean

最终在BeanFactory中呈现的内存情况:

可以看到工厂生产出来的实际对象存放在factoryBeanObjectCache变量中,工厂对象在容器初始化时注册在singletonObjects中;
并且两个变量中的key是相同的;

总结

  • SpringFactoryBean的实现关键在FactoryBeanRegistrySupport类,FactoryBeanRegistrySupport提供了一系列对FactoryBean的支持,其内部维护了一个factoryBeanObjectCache变量作为FactoryBean生产的单例bean的支持。
  • FactoryBean在容器初始化时就会被实例化到BeanFactorysingletonObjects中,FactoryBeangetObject生产出的bean如果是单例,则会存放到factoryBeanObjectCache变量中,便于后续的直接获取。
  • 通过getBean("name")获取的是FactoryBean生产的实际bean对象,通过getBean("&name")获取的是FactoryBean自身对象;

注意需要再配置<bean>的时候加上id值,没有加id值是不能默认通过首字母小写的类名获取到FactoryBean

Spring之FactoryBean的使用与源码解析相关推荐

  1. spring MVC cors跨域实现源码解析

    spring MVC cors跨域实现源码解析 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就是跨域. sp ...

  2. Spring提取@Transactional事务注解的源码解析

    声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记: 难免有错误,敬请读者谅解!!! 1.事务注解标签 <tx:annotation-driven /> 2.tx 命名空间 ...

  3. Spring事务管理的底层逻辑—源码解析

    本文代码为spring 5.1.2 spring是如何控制事务的提交和回滚 加上@Transactional注解之后,Spring可以启到事务控制的功能了,再正式执行方法前它会做一些操作,我们来看看 ...

  4. Spring Security Core 5.1.2 源码解析 -- PasswordEncoderFactories

    概述 PasswordEncoderFactories是Spring Security创建DelegatingPasswordEncoder对象的工厂类.该工厂所创建的DelegatingPasswo ...

  5. Spring 定时任务源码解析

    日常的开发中我们经常需要开发一些定时任务,比较常见的是 Spring 自带的定时任务,使用简单方便,不需要另外引进一些其他 Jar 包. 今天我们来简单的了解一下,看看 Spring 的定时任务是怎么 ...

  6. Dubbo 实现原理与源码解析系列 —— 精品合集

    摘要: 原创出处 http://www.iocoder.cn/Dubbo/good-collection/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1.[芋艿]精尽 Dubbo 原理与源码专栏 2.[ ...

  7. Spring源码解析 - AbstractBeanFactory 实现接口与父类分析

    2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...

  8. Spring Bean的生命周期以及IOC源码解析

    IOC源码这一块太多只能讲个大概吧,建议还是去买本Spring IOC源码解析的书来看比较好,我也是自己看源代码以及视频整理的笔记 Bean的生命周期大概可以分为四个阶段,具体的等会再说,先看看IOC ...

  9. jdk、spring、mybatis、线程的源码分析

    基础篇 从为什么String=String谈到StringBuilder和StringBuffer Java语法糖1:可变长度参数以及foreach循环原理 Java语法糖2:自动装箱和自动拆箱 集合 ...

  10. spring 源码深度解析_spring源码解析之SpringIOC源码解析(下)

    前言:本篇文章接SpringIOC源码解析(上),上一篇文章介绍了使用XML的方式启动Spring,介绍了refresh 方法中的一些方法基本作用,但是并没有展开具体分析.今天就和大家一起撸一下ref ...

最新文章

  1. Mysql 获取当月和上个月第一天和最后一天的解决方案
  2. autosys file watcher 注意事项
  3. JVM性能调优中的命令总结
  4. python多行注释符号_python知识学习,python标识符和关键字
  5. wd my book essential 不显示盘符_江西大规格WD无伸缩短式万向联轴器供应
  6. qt qgis linux,QT_QGIS_基本使用
  7. Deep Q-learning
  8. socket编程-客户端向服务器发送字符串,传文件
  9. 6.相对与绝对目录 cd mkdir rmdir rm
  10. 想做好seo优化,关键词的选择可是重中之重!
  11. hdu 4005 The war
  12. 计算机系统结构概念,计算机系统结构的基本概念
  13. HTML5 CSS3学习
  14. excel中的数据怎么导入matlab中,将excel中的数据导入matlab教程的方法步骤
  15. 一个开源的网页画板,真的太方便了
  16. 米发,免费域名转发 301重定向 URL跳转服务
  17. JQuery拖拽通过八个点改变div大小
  18. win10打开telnet客户端
  19. QtVtk-001-编译
  20. 用计算机画图教案评价,小学四年级信息技术优秀教学设计及评析《电脑图案设计师》...

热门文章

  1. 网站备案其实是服务器备案,网站备案指的是备案域名还是备案主机空间
  2. 中了勒索病毒如何自救?20220105
  3. 26个开发者常用必备网站汇总推荐
  4. 任天堂如何通过旧技术赢得胜利
  5. PyQt5 clicked和clicked[bool]信号区别
  6. PyQt(Python+Qt)学习随笔:Designer中的QDialogButtonBox的clicked信号参数QAbstractButton *解决办法
  7. 嵌入式学习--1线协议(以ds18b20为例)
  8. oracle多表关联查询技巧,Oracle SQL 多表关联查询
  9. oracle多表关联索引用法,关联表查询和索引使用的探讨一则
  10. 对于目标文件系统,文件过大放不到U盘里