聊聊Spring的FactoryBean其实没那么难
前言
谈到Spring的FactoryBean,就会知道Spring中经典的面试题:FactoryBean和BeanFactory的区别。我们这里就简单概括下: 、
BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范,FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式(如果想了解装饰模式参考:修饰者模式(装饰者模式,Decoration) 我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类.
BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似 。
示例
首先我们定义个普通的java pojo对象名叫Car
public class Car {
private int maxSpeed ;
private String brand ;
private double price ;
public double getPrice() {
return price;
}
public int getMaxSpeed() {
return maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"maxSpeed=" + maxSpeed +
", brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
再定义一个实现FactoryBean接口的实现类CarFactoryBean
public class CarFactoryBean implements FactoryBean<Car> {
private String carInfo;
public Car getObject() throws Exception {
System.out.println("start new car");
Car car = new Car();
String[] infos = carInfo.split(",");
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.valueOf(infos[1]));
car.setPrice(Double.valueOf(infos[2]));
return car;
}
public Class<Car> getObjectType(){
return Car.class ;
}
//返回的实例是同一个
public boolean isSingleton() {
return true;
}
public String getCarInfo() {
return this.carInfo ;
}
public void setCarInfo(String carInfo) {
this.carInfo = carInfo;
}
}
再来看看我们的Xml配置是如何配置Bean的
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="car" name="car1 car2" class="com.john.factorybean.CarFactoryBean">
<property name="carInfo" value="超级跑车,400,2000000"/>
</bean>
</beans>
最后我们加载这个配置文件,启动Spring容器来获取car这个Bean
public static void main( String[] args ) {
ClassPathXmlApplicationContext ac =new ClassPathXmlApplicationContext();
ac.setConfigLocations("spring-config-locatorfactory.xml");
ac.refresh();
//getBean的时候才会去getObject
ac.getBean("car", Car.class);
}
成功打印出
start new car
Car{maxSpeed=400, brand='超级跑车', price=2000000.0}
原理
如果我们把ac.getBean("car", Car.class);这行代码去掉,我们会发现start new car这行是不会打印的,那就是说明getObject是延迟加载的,只有显示调用才能触发,spring在初始化启动单例bean实例化的时候并不会触发(SmartFactoryBean除外,之后我们会单独分析下SmartFactoryBean)。
那我们就到源码级别证明下Spring的FactoryBean的getObject是延迟初始化的
DefaultListableBeanFactory
preInstantiateSingletons
@Override
public void preInstantiateSingletons() throws BeansException {
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
//todo 遍历所有不是延迟初始化的 单例 bean 2020-08-28
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//不是抽象的 且是单例的 且不是懒加载的
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//todo 如果是factorybean 那还得去判断 是不是eagerInit
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
//判断是不是渴望初始化的。。
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
}
我们看到Spring会在实例化单例Bean的时候会去判断是否是Factorybean,如果是的话就会去根据FACTORY_BEAN_PREFIX + beanName获取Factorybean本身,然后再判断这个FactoryBean是SmartFactoryBean且isEagerInit设置为true才会去获取beanName对应的Bean,也就是实例化这个bean。
那既然是延迟初始化的,那我们再获取实际bean的时候Spring内部是怎么做的呢?
AbstractBeanFactory
doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// Create bean instance.
//从头开始加载 bean,这个过程由 getSingleton() 实现。
if (mbd.isSingleton()) {
//https://www.cnblogs.com/leihuazhe/p/9481018.html
////todo 如果有单例那直接返回了 , args参数也就没啥用处了 2020-08-30
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly(显式) remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
这里我们从getObjectForBeanInstance方法可以看出来这里就是获取真正bean的方法。
getObjectForBeanInstance
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
//我们使用它来创建一个bean实例,除非调用者实际上想要引用工厂
//todo 如果是获取FactoryBean本身 那就直接返回这个工厂
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
}
也就是说不是FactoryBean本身或者根据name判断就是获取FactoryBean本身的话,那就直接返回这个beanInstance。
那我们在getBean("car")的时候其实获取到的beanInstance是FactoryBean的实例,所以走到这边的时候不会返回。
下面Spring就会走到抽象类FactoryBeanRegistrySupport中的getObjectFromFactoryBean函数,因为AbstractBeanFactory继承自FactoryBeanRegistrySupport,从名字就可以看出这个函数的意义就是调用FactoryBean的getObject方法。
FactoryBeanRegistrySupport
getObjectFromFactoryBean
/**
* Obtain an object to expose from the given FactoryBean.
* @param factory the FactoryBean instance
* @param beanName the name of the bean
* @param shouldPostProcess whether the bean is subject to post-processing
* @return the object obtained from the FactoryBean
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = doGetObjectFromFactoryBean(factory, beanName);
// Only post-process and store if not put there already during getObject() call above
// (e.g. because of circular reference processing triggered by custom getBean calls)
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily(暂时) return non-post-processed object, not storing it yet..
return object;
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
//object放入factoryBeanObjectCache缓存
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
//非单例
省略部分代码
}
}
函数内部还会调用doGetObjectFromFactoryBean函数
doGetObjectFromFactoryBean
/**
* Obtain an object to expose from the given FactoryBean.
* @param factory the FactoryBean instance
* @param beanName the name of the bean
* @return the object obtained from the FactoryBean
* @throws BeanCreationException if FactoryBean object creation failed
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//最重要的调用getObject方法
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
// Do not accept a null value for a FactoryBean that's not fully
// initialized yet: Many FactoryBeans just return null then.
if (object == null) {
if (isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
object = new NullBean();
}
return object;
}
从这个函数中我们看到了FactoryBean自身的getObject方法。调用完getObject方法后,Spring就会把这个对象放入factoryBeanObjectCache缓存对象中。之后再获取的时候直接从factoryBeanObjectCache缓存中拿就是了。
聊聊Spring的FactoryBean其实没那么难的更多相关文章
- block没那么难(二):block和变量的内存管理
本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> 了解了 block的实现,我们接着 ...
- .NET C#到Java没那么难,MVC篇
最典型的JAVA MVC就是JSP + servlet + javabean的模式.比较好的MVC,老牌的有Struts.Webwork.新兴的MVC 框架有Spring MVC.Tapestry.J ...
- 从架构演进的角度聊聊Spring Cloud都做了些什么?
Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cl ...
- 聊聊 Spring Boot 2.x 那些事儿
本文目录: 即将的 Spring 2.0 - Spring 2.0 是什么 - 开发环境和 IDE - 使用 Spring Initializr 快速入门 Starter 组件 - Web:REST ...
- 没那么难,谈CSS的设计模式
没那么难,谈CSS的设计模式 来源: 灵感的小窝 发布时间: 2016-09-09 16:46 阅读: 8949 次 推荐: 27 原文链接 [收藏] 什么是设计模式? 曾有人调侃, ...
- 【小家Spring】聊聊Spring中的数据绑定 --- BeanWrapper以及内省Introspector和PropertyDescriptor
#### 每篇一句 > 千古以来要饭的没有要早饭的,知道为什么吗? #### 相关阅读 [[小家Spring]聊聊Spring中的数据转换:Converter.ConversionService ...
- 【小家Spring】聊聊Spring中的数据绑定 --- DataBinder本尊(源码分析)
每篇一句 唯有热爱和坚持,才能让你在程序人生中屹立不倒,切忌跟风什么语言或就学什么去~ 相关阅读 [小家Spring]聊聊Spring中的数据绑定 --- 属性访问器PropertyAccessor和 ...
- Spring事务专题(五)聊聊Spring事务到底是如何实现的
前言 本专题大纲: 本文为本专题倒数第二篇文章. 在上篇文章中我们一起学习了Spring中的事务抽象机制以及动手模拟了一下Spring中的事务管理机制,那么本文我们就通过源码来分析一下Spring中的 ...
- block没那么难(一):block的实现
本系列博文总结自<Pro Multithreading and Memory Management for iOS and OS X with ARC> block 顾名思义就是代码块,将 ...
随机推荐
- 为什么说线程太多,cpu切换线程会浪费很多时间?
问题1: 假如有一个计算任务,计算1-100的和,每10个数相加,需要占用一个cpu时间片(1s).如果起一个线程(模拟没有线程切换),完成任务需要多长时间?如果起5个线程,完成任务需要消耗多久时间? ...
- web安全原理分析-SQL注入漏洞全解
简介 靶场:榆林学院信息安全协会--入侵榆大实验靶场 数字型注入 1 字符型注入 1 布尔注入 1.布尔注入简介 mysql bool注入是盲注的一种.与报错注入不同,bool注入没有任何报错信息输出 ...
- MFC读写文件详解
1.CFile类提供了对文件进行打开,关闭,读,写,删除,重命名以及获取文件信息等文件操作的基本功能,足以处理任意类型的文件操作. 虽然使用CArchive类内建的序列化功能是保存和加载持久性数据的便 ...
- ABBYY FineReader 12/14版本功能对比及14产品优势
FineReader 是一款一体化的 OCR 和PDF编辑转换器,随着版本的更新,功能的增加,FineReader 14的推出继续为用户在处理文档时提高业务生产力,该版本包含若干新特性和功能增强,包括 ...
- 用MindManager做完思维导图后怎么加密
使用MindManager我们可以制作出很多简洁明了的思维导图,然而,不论是分享还是查看这些导图,我们往往需要对导图进行加密操作,尤其是制定公司或企业的下一步发展计划时,加密导图就显得很有必要了. M ...
- Mac磁盘清理工具——CleanMyMac
许多刚从Windows系统转向Mac系统怀抱的用户,一开始难免不习惯,因为Mac系统没有像Windows一样的C盘.D盘,分盘分区明显.因此这也带来了一些问题,关于Mac的磁盘的清理问题,怎么进行清理 ...
- 苹果电脑怎么给浏览器安装Folx扩展程序
Folx是一款MacOS专用的老牌综合下载管理软件,它的软件界面简洁,应用简单方便,下载管理及软件设置灵活而强大.Folx不但能够进行页面链接下载.Youtube视频下载,而且还是专业的BT下载工具. ...
- Spring 事件监听机制及原理分析
简介 在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现.比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主 ...
- 对KVC和KVO的理解
html { overflow-x: initial !important } :root { --bg-color: #ffffff; --text-color: #333333; --select ...
- pytest测试框架入门
安装pytest 命令行输入: pip install -U pytest 检查是否安装了正确的版本: λ pytest --version This is pytest version 5.3.5, ...