1.概述

目前我们知道,spring创建bean有多种方式,比如xml方式创建,比如@Component,@Service,@Controler,@Repository注解创建,比如@Autowired依赖注入创建,后续还有通过springboot方式的配置注解@Configuration与@Bean方式结合创建,这里不一一介绍,等分析spring boot源码的时候再做总结。

就spring本身,提供了一种接口方式创建bean,就是本节要讨论的通过FactoryBean接口方式创建。

2.实例

FactoryBean接口的实现类FactoryBeanDemo:

package com.wzj.FactoryBean;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Service; @Service
public class FactoryBeanDemo implements FactoryBean { @Override
public Object getObject() throws Exception {
return new FactoryB();
} @Override
public Class<?> getObjectType() {
return FactoryB.class;
}
}

通过FactoryBean实现类,完成自定义类FactoryB的实例化,FactoryB:

package com.wzj.FactoryBean;

import lombok.Data;

@Data
public class FactoryB {
private String name = "wzj";
}

测试类:

public class TestSpring {

    @Autowired
private ApplicationContext applicationContext; @Test
public void testFactoryBean() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
FactoryB factoryB = (FactoryB)applicationContext.getBean("factoryBeanDemo");
System.out.println(factoryB); FactoryBeanDemo factoryBeanDemo = (FactoryBeanDemo)applicationContext.getBean("&factoryBeanDemo");
System.out.println(factoryBeanDemo);
}

测试结果:

可以看出,当获取名称为factoryBeanDemo的实例时,得到的是getObject()方法里创建的FactoryB类型的对象,而获取加前缀&factoryBeanDemo的实例时,得到的是FactoryBeanDemo本身的实例。

3.源码

step1: FactoryBean 接口的调用入口在实例化和 IOC/DI 做完后,就会调用 FactoryBean 类型的接口如下图所示

				// Create bean instance.
// 创建bean实例
if (mbd.isSingleton()) {
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;
}
});
// FactoryBean的调用入口
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

step2: 如果要获取到 FactoryBean 类本身,就必须加上&符号,比如 beanFactory.getBean("&beanName") ,如下:

	protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory.
// 如果为name不为空,且以前缀&打头,直接返回bean本身
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
} // 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.
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
	public static boolean isFactoryDereference(@Nullable String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
	String FACTORY_BEAN_PREFIX = "&";

stet3: BeanFactory.getBean("beanName")只能获取到 getObject()方法返回的实例。getObject 方法返回的实例会有单独的缓存存储,跟其他实例不是同一个缓存,对应的缓存是:factoryBeanObjectCache

                // 如果是不是以前缀&打头,并且是FactoryBean类型的
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 从缓存里拿FactoryBean实例
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 缓存没有的话,
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 先从缓存factoryBeanObjectCache取
Object object = this.factoryBeanObjectCache.get(beanName);
// 如果缓存为空,
if (object == null) {
// 调用getObject方法
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);
}
}
if (containsSingleton(beanName)) {
// 最后放到缓存中
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
......

小结:具体代码参考 getSingleton 方法之后 getObjectForBeanInstance

  • 如果bean实例不是 FactoryBean 类型的或者 name 以&开始的则直接返回实例。
  • 如果bean是 FacotyBean 并且不是以&开头, 会通过方法doGetObjectFromFactoryBean 调用FactoryBean 内部继承实现的 getObject 方法,并且判断一级缓存中如果存在该 bean 实例把实例缓存到factoryBeanObjectCache 对应的 map 中,这个是单独缓存 FactoryBean 类型实例的 map。

4.总结

灵活创建所需实例对象的时候,通过实现FactoryBean接口的getObject方法定义实例化过程。

比如MyBatis提供mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
// ...省略其他代码 public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
} return this.sqlSessionFactory;
}
}

sqlSessionFactory是SqlSessionFactoryBean的一个属性,它的赋值是在通过回调afterPropertiesSet()方法进行的。 因为SqlSessionFactoryBean实现了InitializingBean接口,所以在Spring初始化Bean的时候,能回调afterPropertiesSet()方法。

public void afterPropertiesSet() throws Exception {
// buildSqlSessionFactory()方法会根据mybatis的配置进行初始化。
this.sqlSessionFactory = buildSqlSessionFactory();
}

在上面的afterPropertiesSet()方法中,buildSqlSessionFactory()方法会根据mybatis的配置,完成客户所需要的的sessionFactory的初始化。

【spring源码系列】之【FactoryBean类型的接口】的更多相关文章

  1. Ioc容器依赖注入-Spring 源码系列(2)

    Ioc容器依赖注入-Spring 源码系列(2) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostPr ...

  2. Ioc容器beanDefinition-Spring 源码系列(1)

    Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...

  3. Spring源码系列 — Bean生命周期

    前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...

  4. Spring源码系列(二)--bean组件的源码分析

    简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...

  5. Spring源码系列 — 注解原理

    前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...

  6. Spring源码系列 — BeanDefinition扩展点

    前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition ...

  7. Spring源码系列 — BeanDefinition

    一.前言 回顾 在Spring源码系列第二篇中介绍了Environment组件,后续又介绍Spring中Resource的抽象,但是对于上下文的启动过程详解并未继续.经过一个星期的准备,梳理了Spri ...

  8. 事件机制-Spring 源码系列(4)

    事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...

  9. Ioc容器BeanPostProcessor-Spring 源码系列(3)

    Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...

  10. AOP执行增强-Spring 源码系列(5)

    AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...

随机推荐

  1. 【深度学习 论文篇 01-1 】AlexNet论文翻译

    前言:本文是我对照原论文逐字逐句翻译而来,英文水平有限,不影响阅读即可.翻译论文的确能很大程度加深我们对文章的理解,但太过耗时,不建议采用.我翻译的另一个目的就是想重拾英文,所以就硬着头皮啃了.本文只 ...

  2. ASP.NET Core高性能服务器HTTP.SYS

    如果我们只需要将ASP.NET CORE应用部署到Windows环境下,并且希望获得更好的性能,那么我们选择的服务器类型应该是HTTP.SYS.Windows环境下任何针对HTTP的网络监听器/服务器 ...

  3. LC-283

    题目链接:https://leetcode-cn.com/problems/move-zeroes/ 首先想到了快排(简易思想),0为中间点, 把不等于0(注意题目没说不能有负数)的放到中间点的左边, ...

  4. Grafana插件Plugin中文汉化

    示例Github地址 汉化三方插件 前面说过汉化Grafana的工作.目前在7.2.1上面,大部分已经完成.细节继续完善. 今天考虑在第三方插件上做一些汉化.点到插件一看全是英文感觉很突出.领导看到了 ...

  5. python中的嵌套

    嵌套:将一系列字典存储在列表中,或将列表作为值存储在字典中,这称为嵌套.既可以在列表中嵌套字典,也可以在字典中嵌套列表,甚至在字典中嵌套字典. 一.列表中嵌套字典 1)一般创建方式: student_ ...

  6. ArcGIS使用技巧(三)——关于投影

    新手,若有错误还请指正! 简单记录一下自己所理解的ArcGIS中的有关投影的知识点. 在数据处理过程中,基本都是需要将相关数据放在同一投影坐标系下,需要用到投影转换工具,但若有的数据没有坐标信息,则首 ...

  7. tomcat的搭建和介绍

    第19章 tomcat的搭建 19.1 tomcat学习之前的预备知识 19.1.1 什么是JVM和JDK,JRE JVM            java虚拟机,实现一份代码可以在不同的平台执行,具有 ...

  8. Python学习笔记: pip install 常见错误汇总

    本机环境RHEL8, Python3.9 pip install: 无法安装最新版本的包 在pypi上查看pkg的页面,因为有些pip包的版本对特定的python版本有要求 pip install e ...

  9. Linux中,MySQL的常用命令

    我的博客 登录 mysql -u用户名 -p -- 然后在下面输入密码,Linux的密码不会显示出,盲打就可以 mysql -u用户名 -p密码 -- 这种方式将直接登录 开关 开启数据库 servi ...

  10. Hadoop(四)C#操作Hbase

    Hbase Hbase是一种NoSql模式的数据库,采用了列式存储.而采用了列存储天然具备以下优势: 可只查涉及的列,且列可作为索引,相对高效 针对某一列的聚合及其方便 同一列的数据类型一致,方便压缩 ...