Spring 获取单例流程(三)

简介: 读完这篇文章你将会收获到• Spring 何时将 bean 加入到第三级缓存和第一级缓存中• Spring 何时回调各种 Aware 接口、BeanPostProcessor 、InitializingBean 等

读完这篇文章你将会收获到

  • Spring 何时将 bean 加入到第三级缓存和第一级缓存中
  • Spring 何时回调各种 Aware 接口、BeanPostProcessorInitializingBean


相关文章


概述


上两篇文章 Spring 获取单例流程(一)Spring 获取单例流程(二) 介绍了 getBean 前面的流程,今天最后的收尾,把后面的流程继续一起学习下


源码分析


// 我依赖的大哥都好了
// Create bean instance.
if (mbd.isSingleton()) {
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      } catch (BeansException ex) {
         // 从三级缓存中移除这个 beanName  因为它可能被放进去了 因为放进去三级缓存可以解决 setter 的循环依赖
         destroySingleton(beanName);
         throw ex;
      }
   });
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} 
复制代码


如果我们要创建的 bean 是一个单例,

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   synchronized (this.singletonObjects) {
      // 看看第一级缓存中有没有
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         // 将 beanName 加入到 singletonsCurrentlyInCreation 中,代表它正在创建中
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         try {
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         } catch (IllegalStateException ex) {
            throw ex;
         } catch (BeanCreationException ex) {
            throw ex;
         } finally {
            // singletonsCurrentlyInCreation 从这里面移除掉
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // 加入缓存中
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}
复制代码


删减了部分不重要的代码,我们大致来看看其流程

  1. 获取同步锁,然后判断第一级缓存是否已经存在这个 bean
  2. 如果不存在则将 beanName 加入到 singletonsCurrentlyInCreation 中,代表它正在创建中
  3. 然后调用参数的 ObjectFactorygetObject 方法获得一个 bean
  4. 最后将其从 singletonsCurrentlyInCreation 中移除、代表其已经创建完成了
  5. 最后将其加入到第一级缓存中、从第二级和第三级缓存中移除掉

全篇完结.终 !!!


其实真正的秘密藏身在参数的 ObjectFactory 中,从上面的流程中可以宏观的知道 Spring 创建 bean 的一个流程

现在我们在看看参数的 ObjectFactory  究竟干啥子了

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   ...........
   ...........
   try {
      // 真正 处理逻辑
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      throw ex;
   } catch (Throwable ex) {
      throw new BeanCreationException(xxxx);
   }
}
复制代码


干活的还是 do 开头的大佬

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // 根据指定 bean 使用对应的策略创建新的实例、如工厂方法、构造函数自动注入、简单初始化
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = instanceWrapper.getWrappedInstance();
   Class<?> beanType = instanceWrapper.getWrappedClass();
  ..........
  .........
   // 是否需要提前曝光、用来解决循环依赖的问题
   // 是单例&允许循环依赖&正在创建中
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      // 为了避免后期循环依赖、可以在 bean 初始化前将创建实例的ObjectFactory 加入工厂
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // 填充属性
      populateBean(beanName, mbd, instanceWrapper);
      // 调用初始方法
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   } catch (Throwable ex) {
      .........
   }
   ........
   .......
   return exposedObject;
}
复制代码


上面的流程大致就是

  • createBeanInstance 这个方法根据你的配置以及你的 bean 的情况选择出一种创建 bean 的方法、可能是工厂方法、可能是某个构造函数、可能是默认的构造函数。这里包含了当一个构造函数的参数是另一个 bean 的时候、它会通过 getBean 的方法获取这个参数的 bean
  • 然后将创建好的 bean 加入到第三级缓存中,默认设置我们是允许循环依赖的
  • populateBean 方法就是我们填充属性了、如果你依赖的其他 Spring 的其他 bean 是通过这种方式注入的话(autowireByNameautowireByType )、就是在这一步注入的了,他获取其他 bean 也是通过 getBean的方式获取
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    .........
    .........
      if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
         autowireByName(beanName, mbd, bw, newPvs);
      }
      // Add property values based on autowire by type if applicable.
      if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
         autowireByType(beanName, mbd, bw, newPvs);
      }
    .........
    .........
}
复制代码
  • initializeBean 则是调用我们的各种回调接口、Aware 类型的、BeanPostProcessorInitializingBean、自定义初始化函数
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {。
          // 调用各种 Aware 接口
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   } else {
       // 调用各种 Aware 接口
      invokeAwareMethods(beanName, bean);
   }
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
       // 调用 BeanPostProcessor postProcessBeforeInitialization
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
       // 调用 InitializingBean 、自定义的初始化方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   } catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
       //  调用 BeanPostProcessor postProcessAfterInitialization
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}
复制代码
  • 其实整体的流程就差不多了


总结


  • 根据参数中的 name 找出对应的 beanName、无论这个 name 是别名或者是一个 factoryBeanbeanName
  • 查看缓存中是否包含这个 beanName 对象
  • 先从一级缓存 singletonObjects 中看看有没有
  • 然后从二级缓存 earlySingletonObjects
  • 都没有的话再从三级缓存 singletonFactories 中看看有没有
  • 如果缓存中有 bean、那么我们还是需要处理一下这个 bean
  • 如果 Spring 缓存中返回的 beanfactoryBean 、而用户也想要的是一个 beanFactory (参数 name 中的前缀是 & )、那么我们直接返回
  • 如果 Spring 缓存中返回的 bean 是普通的 bean、而用户也想要的是一个普通的 bean 、那么就直接返回
  • 如果 Spring 缓存中返回的 bean 是一个 factoryBean 、而用户想要的是一个普通的 bean 、那么我们就要从 factoryBean 中获取这个 bean
  • 而从 factoryBean 中获取这个 bean 的过程中、需要调用到前置处理、后置处理和我们常用的接口回调 BeanPostProcessor
  • 如果缓存中没有 bean 、则判断是否是 prototype 类型并且循环依赖
  • 如果没有则尝试能否在父容器中找到该 bean
  • 如果父容器也没有则获取该 beanName 对应的 beanDefinition 找出其依赖的 beanName
  • 判断该 beanName 与 依赖的 beanName 是否循环依赖、没有则注册其依赖关系并调用 getBean 方法去创建依赖的 beanName
  • beanName 加入到 singletonsCurrentlyInCreation
  • 根据指定 bean 使用对应的策略创建新的实例、如工厂方法、构造函数、创建一个不完整的 bean
  • 将创建好的 bean 加入到第三级缓存
  • 进行属性填充、进行各种接口回调
  • 最后将其从 singletonsCurrentlyInCreation 中移除、代表其已经创建完成了
  • 最后将其加入到第一级缓存中、从第二级和第三级缓存中移除掉
  • 返回 bean 给调用方





目录
相关文章
|
6月前
|
缓存 算法 安全
Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
89 0
|
6月前
|
XML 前端开发 Java
深入了解Spring MVC工作流程
深入了解Spring MVC工作流程
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
138 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
25天前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
114 2
|
6月前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
78 1
|
20天前
|
JSON 前端开发 JavaScript
优雅!Spring Boot 3.3 实现职责链模式,轻松应对电商订单流程
本文介绍如何使用 Spring Boot 3.3 实现职责链模式,优化电商订单处理流程。通过将订单处理的各个环节(如库存校验、优惠券核验、支付处理等)封装为独立的处理器,并通过职责链将这些处理器串联起来,实现了代码的解耦和灵活扩展。具体实现包括订单请求类 `OrderRequest`、抽象处理器类 `OrderHandler`、具体处理器实现(如 `OrderValidationHandler`、`VerifyCouponHandler` 等)、以及初始化职责链的配置类 `OrderChainConfig`。
|
3月前
|
安全 Java C#
Spring创建的单例对象,存在线程安全问题吗?
Spring框架提供了多种Bean作用域,包括单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)、全局会话(GlobalSession)等。单例是默认作用域,保证每个Spring容器中只有一个Bean实例;原型作用域则每次请求都会创建一个新的Bean实例;请求和会话作用域分别与HTTP请求和会话绑定,在Web应用中有效。 单例Bean在多线程环境中可能面临线程安全问题,Spring容器虽然确保Bean的创建过程是线程安全的,但Bean的使用安全性需开发者自行保证。保持Bean无状态是最简单的线程安全策略;
|
4月前
|
Java 持续交付 Maven
Spring Boot程序的打包与运行:构建高效部署流程
构建高效的Spring Boot部署流程对于保障应用的快速、稳定上线至关重要。通过采用上述策略,您可以确保部署过程的自动化、可靠性和高效性,从而将专注点放在开发上面。无论是通过Maven的生命周期命令进行打包,还是通过容器技术对部署过程进行优化,选择正确的工具与实践是成功实现这一目标的关键。
170 2
|
5月前
|
Java Spring 容器
解读spring5源码中实例化单例bean的调用链
解读spring5源码中实例化单例bean的调用链
|
5月前
|
安全 NoSQL Java
记录spring security执行流程
Spring Security登录授权流程简述: 1. 实现UserDetailsService,从DB加载用户信息。 2. 创建UserDetails实现类,封装用户详情。 3. 配置WebSecurityConfigurerAdapter,用BCryptPasswordEncoder加密。 4. 设定登录接口为匿名访问。 5. 注入AuthenticationManager,用其authenticate方法认证用户