Spring之FactoryBean的使用与源码解析
文章目录
- 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
中注册了name
为Book
的BeanDefinition
,其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
的实例化关键点,由于前面对FactoryBean
的name
加了"&“前缀,那么注册到BeanFactory
中的单例Bean
的key
是否也是加了”&"符号的呢?
继续看下:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
上面的第一步transformedBeanName(name)
方法会将传入的name转成常规name,即:如果有"&“前缀则将”&"前缀去除。
然后对去除了&的name进行初始化,因为只有这样才能通过转成了常规的name找到BeanDefinition
。
org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
经过以上过程,最终会将FactoryBean
对象注册到BeanFactory
的singletonObjects
属性中,并且name为设置的id值,并不带&符号;
获取FactoryBean实际生产对象过程
那么问题来了,由于singletonObjects
里面注册的Bean
是FactoryBean
对象,并不是实际的bean,那么Spring是怎么调用的是FactoryBean
的getObject
方法返回的呢?
换句话说,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
从上图的过程可以看到factory
的isSingleton
返回的是true
的话,那么会首先从factoryBeanObjectCache
中获取,如果缓存中获取不到则调用doGetObjectFromFactoryBean
方法来实际调用FactoryBean
的getObject
来获取对象,并将对象放入到factoryBeanObjectCache
中用于后续的直接获取。
如果是非单例的话,则直接调用FactoryBean
的getObject
来每次都新创建对象返回。
返回实际生产对象:
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
最终在BeanFactory
中呈现的内存情况:
可以看到工厂生产出来的实际对象存放在factoryBeanObjectCache
变量中,工厂对象在容器初始化时注册在singletonObjects
中;
并且两个变量中的key是相同的;
总结
Spring
的FactoryBean
的实现关键在FactoryBeanRegistrySupport
类,FactoryBeanRegistrySupport
提供了一系列对FactoryBean的支持,其内部维护了一个factoryBeanObjectCache
变量作为FactoryBean
生产的单例bean
的支持。FactoryBean
在容器初始化时就会被实例化到BeanFactory
的singletonObjects
中,FactoryBean
的getObject
生产出的bean如果是单例,则会存放到factoryBeanObjectCache
变量中,便于后续的直接获取。- 通过
getBean("name")
获取的是FactoryBean
生产的实际bean对象,通过getBean("&name")
获取的是FactoryBean
自身对象;
注意需要再配置<bean>
的时候加上id值,没有加id值是不能默认通过首字母小写的类名获取到FactoryBean
的
Spring之FactoryBean的使用与源码解析相关推荐
- spring MVC cors跨域实现源码解析
spring MVC cors跨域实现源码解析 名词解释:跨域资源共享(Cross-Origin Resource Sharing) 简单说就是只要协议.IP.http方法任意一个不同就是跨域. sp ...
- Spring提取@Transactional事务注解的源码解析
声明:本文是自己在学习spring注解事务处理源代码时所留下的笔记: 难免有错误,敬请读者谅解!!! 1.事务注解标签 <tx:annotation-driven /> 2.tx 命名空间 ...
- Spring事务管理的底层逻辑—源码解析
本文代码为spring 5.1.2 spring是如何控制事务的提交和回滚 加上@Transactional注解之后,Spring可以启到事务控制的功能了,再正式执行方法前它会做一些操作,我们来看看 ...
- Spring Security Core 5.1.2 源码解析 -- PasswordEncoderFactories
概述 PasswordEncoderFactories是Spring Security创建DelegatingPasswordEncoder对象的工厂类.该工厂所创建的DelegatingPasswo ...
- Spring 定时任务源码解析
日常的开发中我们经常需要开发一些定时任务,比较常见的是 Spring 自带的定时任务,使用简单方便,不需要另外引进一些其他 Jar 包. 今天我们来简单的了解一下,看看 Spring 的定时任务是怎么 ...
- Dubbo 实现原理与源码解析系列 —— 精品合集
摘要: 原创出处 http://www.iocoder.cn/Dubbo/good-collection/ 「芋道源码」欢迎转载,保留摘要,谢谢! 1.[芋艿]精尽 Dubbo 原理与源码专栏 2.[ ...
- Spring源码解析 - AbstractBeanFactory 实现接口与父类分析
2019独角兽企业重金招聘Python工程师标准>>> 我们先来看类图吧: 除了BeanFactory这一支的接口,AbstractBeanFactory主要实现了AliasRegi ...
- Spring Bean的生命周期以及IOC源码解析
IOC源码这一块太多只能讲个大概吧,建议还是去买本Spring IOC源码解析的书来看比较好,我也是自己看源代码以及视频整理的笔记 Bean的生命周期大概可以分为四个阶段,具体的等会再说,先看看IOC ...
- jdk、spring、mybatis、线程的源码分析
基础篇 从为什么String=String谈到StringBuilder和StringBuffer Java语法糖1:可变长度参数以及foreach循环原理 Java语法糖2:自动装箱和自动拆箱 集合 ...
- spring 源码深度解析_spring源码解析之SpringIOC源码解析(下)
前言:本篇文章接SpringIOC源码解析(上),上一篇文章介绍了使用XML的方式启动Spring,介绍了refresh 方法中的一些方法基本作用,但是并没有展开具体分析.今天就和大家一起撸一下ref ...
最新文章
- Mysql 获取当月和上个月第一天和最后一天的解决方案
- autosys file watcher 注意事项
- JVM性能调优中的命令总结
- python多行注释符号_python知识学习,python标识符和关键字
- wd my book essential 不显示盘符_江西大规格WD无伸缩短式万向联轴器供应
- qt qgis linux,QT_QGIS_基本使用
- Deep Q-learning
- socket编程-客户端向服务器发送字符串,传文件
- 6.相对与绝对目录 cd mkdir rmdir rm
- 想做好seo优化,关键词的选择可是重中之重!
- hdu 4005 The war
- 计算机系统结构概念,计算机系统结构的基本概念
- HTML5 CSS3学习
- excel中的数据怎么导入matlab中,将excel中的数据导入matlab教程的方法步骤
- 一个开源的网页画板,真的太方便了
- 米发,免费域名转发 301重定向 URL跳转服务
- JQuery拖拽通过八个点改变div大小
- win10打开telnet客户端
- QtVtk-001-编译
- 用计算机画图教案评价,小学四年级信息技术优秀教学设计及评析《电脑图案设计师》...
热门文章
- 网站备案其实是服务器备案,网站备案指的是备案域名还是备案主机空间
- 中了勒索病毒如何自救?20220105
- 26个开发者常用必备网站汇总推荐
- 任天堂如何通过旧技术赢得胜利
- PyQt5 clicked和clicked[bool]信号区别
- PyQt(Python+Qt)学习随笔:Designer中的QDialogButtonBox的clicked信号参数QAbstractButton *解决办法
- 嵌入式学习--1线协议(以ds18b20为例)
- oracle多表关联查询技巧,Oracle SQL 多表关联查询
- oracle多表关联索引用法,关联表查询和索引使用的探讨一则
- 对于目标文件系统,文件过大放不到U盘里