FactoryBean源码解析

文章目录

一、简介

FactoryBean 是 Spring 框架中的一个接口,用来创建特定类型的 Bean 对象。实现FactoryBean 接口就可以自定义 Bean 对象的创建过程。FactoryBean 本身也是 Bean,会在 Spring 中经历 Bean 的生命周期,而由 FactoryBean 创建的 Bean 不经历 Bean 的生命周期。

二、FactoryBean 接口的方法

FactoryBean 内部包含了一下三个方法:

  • getObject()FactoryBean 最重要的方法,用于返回被管理的Bean对象的实例。
  • getObjectType():返回的 Bean 对象的类型。
  • isSingleton():用于指示返回的 Bean 对象是否为单例。

三、FactoryBean 与 BeanFactory 的区别

有的人分不清 FactoryBeanBeanFactory ,其实只要是粗略看过 Spring 源码的人,都会知道,BeanFactory 是 Spring 容器的顶层接口,这个接口定义了 Spring 容器最基本的功能,如从容器中获取 Bean,判断 Bean 是不是单例等,归根结底它是一个工厂,或者是一个容器,用来创建和管理 Bean 的,而 FactoryBean 是一种特殊的 Bean,注意它是个 Bean,只不过它让我们可以自定义创建 Bean 的过程。

四、源码解析

首先我们先找到 Spring 启动时,初始化 Bean 的核心方法,位于 AbstractApplicationContextrefresh 方法里的 finishBeanFactoryInitialization(beanFactory),在这个方法里,找到最后一行最核心的方法 beanFactory.preInstantiateSingletons() ,在这个方法里,找到下面这段代码。

这段代码是从 Spring 容器里获取所有的非抽象的单例的非懒加载 的 Bean,然后判断是不是 FactoryBean,如果不是,就直接调用 getBean 方法完成初始化,如果是,就判断是不是需要紧急初始化。

这里需要注意,FactoryBean 也是做为一个 Bean 放在 Spring 容器里的,从容器里获取这个 FactoryBean 实例,需要在名称前加上 & 符号,如果不加的话,获取的是 FactoryBean 创建出来的 Bean 实例,所以这里通过 Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); 这样获取,这个 FACTORY_BEAN_PREFIX 就是 & 符号。

另外这里出现了 SmartFactoryBean 这个接口,这个接口是 FactoryBean 的子接口,其内部通过 isEagerInit 方法来控制是否紧急初始化,所以这里代码的意思就是,如果是 SmartFactoryBean 类型的 FactoryBean 并且 isEagerInit 是 true 的,才会立即调用 getBean 方法完成初始化。

我们继续跟着 getBean 方法往里看

继续跟进 doGetBean,找到创建 Bean 实例的核心代码

在调用完 getSingleton 方法后,会将 FactoryBean 实例放到 Spring 容器里,再调用 getObjectForBeanInstance 方法获取 Bean 实例,我们跟进去看看

继续跟进

第一个if判断,用处是防止那种不是 FactoryBean,但是前面带了 & 符号进来的。

第二个if判断,就是如果是普通的 bean 或者是 FactoryBean,就返回其实例本身,这就是我们上面说的,如果 FactoryBeanbeanName 前面带了 & 符号,就返回 FactoryBean 本身。

能继续往下走,就说明一定是 FactoryBean 了,所以类型强转后,调用 getObjectFromFactoryBean 方法,我们继续跟进

这里用了一些缓存的机制,我们不用管,继续跟进 doGetObjectFromFactoryBean 方法

我们可以看到,最终调用了 FactoryBeangetObject 方法

五、实际应用

FactoryBean 的实际应用场景有很多,例如 SpringAop 的 ProxyFactoryBean 用来创建 AOP 代理,Mybatis 的 MapperFactoryBean 用来创建 Mapper 的代理对象,Fegin 的 FeignClientFactoryBean 用来创建远程服务代理。

有兴趣的小伙伴,可以在任意一个 FactoryBeangetObject() 方法上打上断点,启动服务,看看具体的方法调用栈,可以更好的帮助学习源码。

相关推荐
Dola_Pan33 分钟前
Linux文件IO(二)-文件操作使用详解
java·linux·服务器
wang_book36 分钟前
Gitlab学习(007 gitlab项目操作)
java·运维·git·学习·spring·gitlab
weixin_455446171 小时前
Python学习的主要知识框架
开发语言·python·学习
蜗牛^^O^2 小时前
Docker和K8S
java·docker·kubernetes
从心归零2 小时前
sshj使用代理连接服务器
java·服务器·sshj
一个诺诺前行的后端程序员3 小时前
springcloud微服务实战<1>
spring·spring cloud·微服务
IT毕设梦工厂3 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Ylucius4 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
LvManBa4 小时前
Vue学习记录之六(组件实战及BEM框架了解)
vue.js·学习·rust