一、FactoryBean 用法讲解

在分析源码流程之前,我们先来看一下 FactoryBean,乍一看这家伙和 BeanFactory 很像,它们都可以用来获取 bean 对象,简单来说 FactoryBean 是一种可以生产 bean 的 bean,而 FactoryBean 是一个生产 bean 的工厂。

下面举个例子来简单说明一下 BeanFactory 的用法:

    // 定义一个 User@Dat@Builderpublic class User {private String name;private Integer age;}// 定义一个 FactoryBean 用来创建 User 实public class UserFactoryBean implements FactoryBean<User> {@Overridepublic User getObject() {return User.builder().name("jas").age(18).build();}@Overridepublic Class<?> getObjectType() {return User.class;}}// 在 spring 的配置文件中配置 FactoryBean 实<bean id="userFactoryBean" class="com.jas.mess.factory.UserFactoryBean"/>// 编写测试类@Testpublic void factoryBeanTest() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);System.out.println("userFactoryBean -> " + applicationContext.getBean("userFactoryBean", User.class));// & 可以用于获取 FactoryBean 本身UserFactoryBean userFactoryBean = applicationContext.getBean("&userFactoryBean", UserFactoryBean.class);System.out.println("&userFactoryBean -> " + userFactoryBean);System.out.println("userFactoryBean objectType -> " + userFactoryBean.getObjectType());}

输出结果如下:

从上面的输出结果中可以看出 FactoryBean 可以用来创建其他类型的 bean,如果想要获取 FactoryBean 实例本身,需要给 beanName 加上 & 前缀。

从 Spring IoC 容器获取 bean 的流程里有关于 FactoryBean 的处理,上面知道了 FactoryBean 的用法,下面一起来看下具体的流程细节吧!

二、getBean 流程分析

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {/*** 这里的 name 有三种情况:* 1.beanName 本身,即:<id="" class=""/> 中 id 的值* 2.'&' 开头,表示获取 FactoryBean 本身* 3.alias 别名** 如果 beanName 以 `&` 开头,则去除 `&` 前缀,如果是 alias 则替换为对应的 beanName*/final String beanName = transformedBeanName(name);Object bean;// 单例模式的 Bean 在整个过程中只会被创建一次,第一次创建后会将该 Bean 加载到缓存中,再获取 Bean 就会从缓存中获取Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}/*** 注意这个 name 是没有经过处理的,有可能以 `&` 开头,即获取 FactoryBean 本身,而不是对应的 bean 实例* 当然如果是获取 FactoryBean 本身则会直接返回*/bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {/*** 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常* Spring 只能解决单例模式的循环依赖,为什么呢?因为单例模式下有对象缓存*/if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 单例缓存中没有实例,则可能表明该实例还没有创建或者该实例在父容器中已经创建了,所以需要先检查一次父容器BeanFactory parentBeanFactory = getParentBeanFactory();// parentBeanFactory 不为空且 beanDefinitionMap 中已经保存过 beanName 对应的 BeanDefinitionif (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// 获取 name 对应的 beanName,如果 name 是以 & 字符开头,则返回 & + beanNameString nameToLookup = originalBeanName(name);// 如果父类容器为 AbstractBeanFactory ,则调用 doGetBean 获取 beanif (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}// 用明确的 args 从 parentBeanFactory 中,获取 Bean 对象else if (args != null) {return (T) parentBeanFactory.getBean(nameToLookup, args);}// 用明确的 requiredType 从 parentBeanFactory 中,获取 Bean 对象else if (requiredType != null) {return parentBeanFactory.getBean(nameToLookup, requiredType);}// 直接使用 beanName 从 parentBeanFactory 获取 Bean 对象else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {// 标记 bean 创建完成或将要创建,主要用来缓存markBeanAsCreated(beanName);}try {// 合并父 BeanDefinition 与子 BeanDefinition(父子容器根据 beanName 进行属性合并)final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// 检查给定的合并的 beanDefinitioncheckMergedBeanDefinition(mbd, beanName, args);// 获取依赖的 beanString[] dependsOn = mbd.getDependsOn();/*** 每个 Bean 都不一定是单独工作的,它可能会依赖其他 Bean,其他 Bean 也会依赖它* 对于依赖的 Bean ,它会优先加载,所以,在 Spring 的加载顺序中,* 在初始化某一个 Bean 的时候,首先会初始化这个 Bean 的依赖** 注意这种依赖不是 bean 属性相互依赖,与我们常说的循环依赖是两种类型*** <bean id="beanA" class="BeanA" depends-on="beanB">* <bean id="beanB" class="BeanB" depends-on="beanA">** 对于上面这种依赖关系会抛出异常*/if (dependsOn != null) {for (String dep : dependsOn) {// 如果存在循环依赖,则抛出异常if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注入依赖的 bean 存储在对应的 map 中,记录彼此依赖的关系registerDependentBean(dep, beanName);try {// 内部通过调用 doGetBean 方法 加载 depends-on 依赖的 beangetBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}/*** Spring Bean 的作用域默认为 singleton ,当然还有其他作用域,如 prototype、request、session 等* 不同的作用域会有不同的初始化策略* 如果是单例模式,因为刚开始是从单例缓存中获取,如果缓存中不存在,则需要从头开始加载*/if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {destroySingleton(beanName);throw ex;}});// 处理 FactoryBean 类型的 beanbean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 原型模式else if (mbd.isPrototype()) {Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 从指定的 scope 下创建 beanelse {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// 检查 bean 的实际类型是否符合需要的类型if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}

getBean 的主要流程就是上面这些,下面根据步骤来总结一下,对于一些比较重要的方法,下面会进行详细分析。

  1. 换 name 为 beanName,如果是别名,则循环处理获取最终指向的 beanName,如果以 & 开头,则剥离 & 前缀
  2. 根据 beanName 从单例缓存中获取 bean,如果 bean 不为空,则调用 getObjectForBeanInstance 方法
  3. 缓存中没有则获取父容器,父容器存在时尝试从父容器中获取 bean
  4. 父容器中没有该实例时,则合并父子的 BeanDefinition,接着处理 depends-on 的依赖关系
  5. 根据不同的类型创建 bean
  6. 创建 bean 时如果指定了类型,则对 bean 进行类型转换

下面对一些重要的方法单独拿出来分析,帮助大家理解。

2.1getSingleton

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;}

getSingleton 方法涉及到循环依赖问题,有兴趣的可以找下上篇文章,有详细解释。这里涉及到三个缓存 map,其中有一个就是用来解决循环依赖的,这里分别介绍一下。

  • singletonObjects:缓存实例化完成的单例 bean切属性被注入
  • earlySingletonObjects:缓存实例化完成,但还未注入属性的 bean,用于提前曝光 bean
  • singletonFactories:缓存初始化完成,属性未注入的 bean,当 bean 提前曝光(缓存到 earlySingletonObjects 中)以后,会删除该缓存,可以用来解决循环依赖

2.2 getObjectForBeanInstance

在一开始的时候我们先了解了 FactoryBean,下面的逻辑就会处理到。

    protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {// 判断 nam 是否以 '&' 开头,如果是表示获取 FactoryBean 本身,而不是对应的 bean 实例if (BeanFactoryUtils.isFactoryDereference(name)) {// 如果是 NullBean 直接返回if (beanInstance instanceof NullBean) {return beanInstance;}// 已 & 开头,如果不是 FactoryBean 类型抛出异常if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());}}// 该实例可能是会是一个正常的 bean 又或者是一个 FactoryBean 本身,如果是则直接返回if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {return beanInstance;}Object object = null;/*** 如果 beanDefinition 为 null,则从 factoryBeanObjectCache 缓存中获取 bean* FactoryBean 生成的单例 bean 会被缓存在 factoryBeanObjectCache 集合中,不用每次都创建*/if (mbd == null) {object = getCachedObjectForFactoryBean(beanName);}// 如果代码执行这里则可以确认,beanInstance 一定是 FactoryBean 类型if (object == null) {// 转换成 FactoryBean 类型FactoryBean<?> factory = (FactoryBean<?>) beanInstance;// Caches object obtained from FactoryBean if it is a singleton.if (mbd == null && containsBeanDefinition(beanName)) {// 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition,// 合并 BeanDefinitionmbd = getMergedLocalBeanDefinition(beanName);}// 检测是用户定义的还是程序本身定义的boolean synthetic = (mbd != null && mbd.isSynthetic());// 从 FactoryBean 中获取实例object = getObjectFromFactoryBean(factory, beanName, !synthetic);}return object;}
  1. 判断是否是 FactoryBean 类型,如果以 & 开头切不是 FactoryBean 类型抛出异常
  2. 如果是普通类型的 bean,获取要获取 BeanFactory 本身,则直接返回
  3. FactoryBean 中获取实例

getObjectFromFactoryBean 方法还有一些流程,这里就不赘述了,有兴趣的自己可以研究下。

参考

Spring IOC 容器源码分析 - 获取单例 bean by 田小波

Spring IoC 源码系列(五)getBean 流程分析相关推荐

  1. Spring IoC 源码系列(四)bean创建流程与循环依赖问题分析

    创建单例 bean 的代码细节在 org.springframework.beans.factory.support.AbstractBeanFactory#getBean 中,getBean 顾名思 ...

  2. Spring IoC 源码系列(一)BeanDefinition 初始化与注册

    一.BeanDefinition 1.1 什么是 BeanDefinition 在一般的 Spring 项目中,主要通过 XML 的方式配置 bean,而 BeanDefinition 就是 XML ...

  3. Spring IoC 源码系列(三)Spring 事件发布机制原理分析

    在 IoC 容器启动流程中有一个 finishRefresh 方法,具体实现如下: protected void finishRefresh() {clearResourceCaches();init ...

  4. Spring IoC 源码导读

    源码记录:spring-framework-5.1.7-source-code-read 文章导读 Spring IoC 源码系列(一)BeanDefinition 初始化与注册 Spring IoC ...

  5. Spring IoC源码:getBean 详解

    文章目录 Spring源码系列: 前言 正文 方法1:getObjectForBeanInstance 方法2:getObjectFromFactoryBean 方法3:doGetObjectFrom ...

  6. Spring读源码系列之AOP--03---aop底层基础类学习

    Spring读源码系列之AOP--03---aop底层基础类学习 引子 Spring AOP常用类解释 AopInfrastructureBean---免被AOP代理的标记接口 ProxyConfig ...

  7. Android 源码 Camera2 预览流程分析四

    <Android 源码 Camera2 预览流程分析二>中进行了流启动,这是调用 QCamera3Channel start() 方法实现的,对应于 HAL_PIXEL_FORMAT_YC ...

  8. Spring AOP 源码系列(一)解析 AOP 配置信息

    在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读. 关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐 ...

  9. 【源码】Spring IOC源码

    从Spring为什么要用IoC的支点,我撬动了整个Spring的源码脉络! - 掘金 https://juejin.cn/post/7124482061364609038 seaswalker/spr ...

最新文章

  1. C语言——冒泡法排序应用
  2. Linux下基于官方源代码RPM包构建自定义MySQL RPM包
  3. 理解并解决IE的内存泄漏方式[翻译2]
  4. python入门教程完整版(懂中文就能学会)-Python入门教程完整版400集(懂中文就能学会)快来带走...
  5. C++睡眠延时函数 Sleep() usleep()(windows.h、unistd.h)
  6. 博士申请 | 香港中文大学(深圳)纪冬旭老师招收博士生/研究助理/博士后
  7. SFB 项目经验-05-共存迁移-Lync 2013-SFB 2015-边缘服务器复制状态不正常
  8. 亚伦•斯沃茨:怎样有效利用时间
  9. android+阴影+xml,Android 阴影视图 ShadowViewHelper
  10. 【RK3399Pro学习笔记】七、ROS订阅者Subscriber的编程实现
  11. 使用junit+mockito进行mock测试实例
  12. 给.net初学者的一些建议(共勉之)[转载]
  13. java运行字符串代码
  14. mybatis多对一,一对一,多对多resultMap映射,pojo映射,传参集合,封装的对象传参
  15. Linux中的samba服务和ftp服务
  16. MySQL(10)-----多表创建及描述表关系(一对多的分析和实现)
  17. 极域教室管理软件全屏广播窗口化,解除网络限制,阻止老师监控进程,阻止黑屏安静,强制杀死极域进程等
  18. Maxwell参数化建模和优化设计
  19. wave overfly
  20. LibLand摄相头驱动 for Linux

热门文章

  1. sleep方法和wait方法的区别
  2. JavaScript——易班优课YOOC课群在线测试自动答题解决方案(九)ID标签
  3. C#——委托(delegate)DEMO
  4. 浅谈主流内存发展历史
  5. Linux(CentOS 7)——阿里云 云服务器 ECS上Apache服务器安装与配置
  6. BugKuCTF 加密 easy_crypto
  7. 相关疑惑解决,java线程虚假唤醒等等问题
  8. mongodb创建用户
  9. spark sql and hive 3g数据测试
  10. 前端之旅,正式启航~【前端学习路线图+配套学习视频+师长指点】