Spring 获取单例流程(一)

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

  • getBean 方法中, Spring 处理别名以及 factoryBeanname
  • Spring 如何从多级缓存中根据 beanName 获取 bean
  • Spring 如何处理用户获取普通 beanfactoryBean

引言

Spring 容器的初始化 中,咱们了解到 Spring 是如何将 XML 文件转换为 BeanDefinition 并注册到 BeanDefinitionRegstryspring

今天咱们一块儿继续学习 Springbean 加载缓存

public static void main(String[] args) {
 Resource resource = new ClassPathResource("coderLi.xml");  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);  xmlBeanDefinitionReader.loadBeanDefinitions(resource);  } 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans>  <bean class="com.demo.data.Person">  <description>  微信搜一搜:CoderLi  </description>  </bean> </beans> 复制代码

熟悉的味道、熟悉的配方微信

源码分析

咱们能够在上面的 Java 代码中加入如下的代码并发

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
复制代码

咱们根据默认的 beanName 从 Spring 容器中获取 Person 这个 bean 对象,固然咱们可使用默认的别名获取app

System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
复制代码

对 Spring 别名不熟悉的朋友能够先看下个人这一篇文章 Spring-AliasRegistry编辑器

咱们直接进入到 AbstractBeanFactory#getBean(String) 方法中, AbstractBeanFactoryDefaultListableBeanFactory 的父类ide

@Override
public Object getBean(String name) throws BeansException {  return doGetBean(name, null, null, false); } 复制代码

能够看到 do 开头的才是真正干活的老大 , AbstractBeanFactory#doGetBean源码分析

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,  @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {   // 找到这个参数的 bean name  final String beanName = transformedBeanName(name);  Object bean;   // Eagerly check singleton cache for manually registered singletons.  // 检查缓冲中是否有这个bean、spring中只是保存单例的bean  Object sharedInstance = getSingleton(beanName);  if (sharedInstance != null && args == null) {  // 这里被我删除了一些spring 的log  // 处理一下 factory bean 的状况、包括从 factory beans 的缓存中获取、或者从新调用 factory bean 的 get bean 方法 包括一些回调  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  }  ..........  ........... 复制代码

由于这个方法实在太长了因此截取一部分、咱们一步步来分析post

transformedBeanName(name) 这个方法就是将咱们的 name 转换为真正的 beanName,由于咱们传进来的参数多是一个 alias 或者多是一个 factoryBean 的 beanName (前缀为&),而咱们在 Spring 中存放的 factoryBean 的 beanName 是没有 & 前缀的,因此须要处理掉这个前缀

protected String transformedBeanName(String name) {
 return canonicalName(BeanFactoryUtils.transformedBeanName(name)); } public static String transformedBeanName(String name) {  Assert.notNull(name, "'name' must not be null");  // 是不是一个 factory bean 、若是不是的话就直接返回  if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {  return name;  }  // 若是是的话就将其前缀 & 去掉  return transformedBeanNameCache.computeIfAbsent(name, beanName -> {  do {  beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());  }  while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));  return beanName;  });  }  /**  * 找到这个别名的最终的 bean Name、若是没有的话(  * 也就是说参数中的name 就是人家的 bean name 那么就直接返回这个 参数就好了)  *  */  public String canonicalName(String name) {  String canonicalName = name;  // Handle aliasing...  String resolvedName;  do {  resolvedName = this.aliasMap.get(canonicalName);  if (resolvedName != null) {  canonicalName = resolvedName;  }  }  while (resolvedName != null);  return canonicalName;  } 复制代码

让咱们再看看下一个方法 DefaultSingletonBeanRegistry#getSingleton(String)

public Object getSingleton(String beanName) {
 // allowEarlyReference 容许早期依赖  return getSingleton(beanName, true); } 复制代码
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {   Object singletonObject = this.singletonObjects.get(beanName);  // 这个bean 正处于 建立阶段  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {  // 并发控制  synchronized (this.singletonObjects) {  // 单例缓存是否存在  singletonObject = this.earlySingletonObjects.get(beanName);  // 是否运行获取 bean factory 建立出的 bean  if (singletonObject == null && allowEarlyReference) {  // 获取缓存中的 ObjectFactory  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  if (singletonFactory != null) {  singletonObject = singletonFactory.getObject();  // 将对象缓存到 earlySingletonObject中  this.earlySingletonObjects.put(beanName, singletonObject);  // 从工厂缓冲中移除  this.singletonFactories.remove(beanName);  }  }  }  }  return singletonObject; } 复制代码

上面的代码就是 Spring 尝试从缓存中加载单例。单例在 Spring 的同一个容器中只会被建立一次,后续再获取 bean,就直接从缓存中取了。

在介绍这个方法以前、咱们先认识下 DefaultSingletonBeanRegistry 这个类里面的成员变量吧

  • Map<String, Object> singletonObjects 这个很好理解、 key 就是 beanName ,value 就是 bean 实例
  • Map<String, ObjectFactory<?>> singletonFactories key 为 beanName,value 为建立 bean 的工厂
  • Map<String, Object> earlySingletonObjects key 为 beanName ,value 为 bean。可是和 singletonObjects 不一样的是,bean 被加入到 earlySingletonObjects 的时候、这个 bean 仍是处于一种建立中的状态,目的也很简单、Spring 用来解决某些场景下的循环依赖

咱们再回到代码中、分析一下它的逻辑

  1. 先从 singletonObjects 中尝试获取 bean,这里存放的是已经建立好的 bean 了、若是在这里能知道、那固然是最好啦
  2. 若是在这里找不到的话、那么咱们就要判断下这个 beanName 对应的 bean 是否正在建立中
  3. 若是是的话,那么咱们再看看这个正在建立的 bean 是否已经曝光出来、若是没有的话、那么就要看看咱们的参数是否容许依赖早期的 bean 了、
  4. 若是容许早期依赖、那么咱们就尝试冲 ObjectFactory 中获取到对应的 bean、并将它放入到 earlySingletonObjects 中、并从 singletonFactories 中移除

相似多级缓存的设计

在上面的方法中咱们看到 isSingletonCurrentlyInCreation(beanName) 这个方法、

public boolean isSingletonCurrentlyInCreation(String beanName) {
 return this.singletonsCurrentlyInCreation.contains(beanName); } 复制代码

singletonsCurrentlyInCreation 这个 Set 中,当建立一个 bean 以前会将其 对应的 beanName 放置到这个 Set 中、后面的分析会涉及到、这里先提一嘴

debug
debug

咱们第一次获取这个 bean 、返回为 null 是正常的

那假如咱们在代码中 getBean 了两次

defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0") 复制代码

那么针对第二次的调用、返回的值就不是为 null 了

Object sharedInstance = getSingleton(beanName);
 if (sharedInstance != null && args == null) {  // 处理一下 factory bean 的状况、包括从 factory beans 的缓存中获取、或者从新调用 factory bean 的 get bean 方法 包括一些回调  bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  } 复制代码

咱们先假设 sharedInstance 不为 null 也就是咱们第二次调用 getBean ,咱们进入到 getObjectForBeanInstance 方法中

protected Object getObjectForBeanInstance(  Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {   // 我王大锤就是想要一个 factory bean  if (BeanFactoryUtils.isFactoryDereference(name)) {   // 若是这个是 NullBean 类型、表示这是一个 null 的 instance、直接返回  if (beanInstance instanceof NullBean) {  return beanInstance;  }  // 获取到的 beanInstance 不是一个 factory、可是你tm name 又带有这个 & 很迷惑啊兄弟  if (!(beanInstance instanceof FactoryBean)) {  throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());  }   if (mbd != null) {  mbd.isFactoryBean = true;  }  return beanInstance;  }   // 王大锤不想要factory bean、而且spring 也帮他找到了一个普通的 bean、直接返回  if (!(beanInstance instanceof FactoryBean)) {  return beanInstance;  }   // 王大锤要的是一个普通的bean 、可是spring 给他找到了一个 factory的bean、那么spring 是否是要作一些额外的处理 给王大锤返回一个普通的bean  Object object = null;   if (mbd != null) {  mbd.isFactoryBean = true;  } else {  // 从缓存中 看看有没有  object = getCachedObjectForFactoryBean(beanName);  }   // 若是 bean factory 中仍是没有  if (object == null) {  // Return bean instance from factory.  FactoryBean<?> factory = (FactoryBean<?>) beanInstance;  // 从 缓存中拿到 bean definition  if (mbd == null && containsBeanDefinition(beanName)) {  mbd = getMergedLocalBeanDefinition(beanName);  }  // 是不是用户定义的仍是程序自己须要建立的bean  boolean synthetic = (mbd != null && mbd.isSynthetic());   object = getObjectFromFactoryBean(factory, beanName, !synthetic);  }  return object; } 复制代码

咱们按步骤分析下上面的代码

  1. 咱们调用 getBean(name) 中的 name 若是包含前缀 & ,表面咱们是想要从 Spring 中获取一个 FactoryBean ,那么咱们就要判断咱们从缓存中获取的 beanInstance 是不是 一个 FactoryBean 、若是是的话就直接返回不是的话就要抛出异常了
  2. 咱们想要的是一个非 factoryBean 而且 在 spring 容器中找到了非 factoryBean 的 bean、那么就直接返回
  3. 咱们想要的是一个 非 factoryBean 可是在 spring 容器中找到了一个 factoryBean 的 bean、那么就要进入到 getObjectFromFactoryBean 方法中了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
 // 为单例模式且缓存中存在  if (factory.isSingleton() && containsSingleton(beanName)) {   synchronized (getSingletonMutex()) {  // 从缓存中获取指定的 bean(这个bean 是从 factory bean 建立出来的)  Object object = this.factoryBeanObjectCache.get(beanName);   if (object == null) {  // 为空则从 factory bean 中获取对象  object = doGetObjectFromFactoryBean(factory, beanName);  // 从缓存中获取  Object alreadyThere = this.factoryBeanObjectCache.get(beanName);  if (alreadyThere != null) {  // 已经存放到 缓存中了、后续的操做就不须要了  object = alreadyThere;  } else {  // 须要作一些后置处理  if (shouldPostProcess) {  // 若是这个bean正在建立中、  if (isSingletonCurrentlyInCreation(beanName)) {  return object;  }  // 前置处理 主要是将这个bean 加入到正在建立中的队列 singletonsCurrentlyInCreation  beforeSingletonCreation(beanName);  try {  // 对 从 factoryBean 获取的对象进行后处理  // 生成对象将暴露给 bean 引用 并回调 beanPostProcessor  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName,  "Post-processing of FactoryBean's singleton object failed", ex);  } finally {  // 后置处理 将其从 singletonsCurrentlyInCreation 移除  afterSingletonCreation(beanName);  }  }  // 他的 factory bean 已经存在 缓存中了、那么这个 factory bean 产生的bean 应该也要缓存一下  if (containsSingleton(beanName)) {  this.factoryBeanObjectCache.put(beanName, object);  }  }  }   return object;  }  } else {  // 非单例  Object object = doGetObjectFromFactoryBean(factory, beanName);   if (shouldPostProcess) {  try {  //  object = postProcessObjectFromFactoryBean(object, beanName);  } catch (Throwable ex) {  throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);  }  }  return object;  } } 复制代码

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊、代码很长长长..........咱们一点点来分析

  1. 第一步就是判断这个 factoryBean 是不是单例、若是不是的话,而且是用户本身定义的 bean、那么就须要调用 postProcessObjectFromFactoryBean 方法去作一个后续的处理

    1. 这里面最终回调的就是咱们经常使用的一个接口 BeanPostProcessor
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)  throws BeansException {   Object result = existingBean;  for (BeanPostProcessor processor : getBeanPostProcessors()) {  Object current = processor.postProcessAfterInitialization(result, beanName);  if (current == null) {  return result;  }  result = current;  }  return result; } 复制代码
  2. 若是这 beanFactory 是一个单例,那咱们就看看 factoryBeanObjectCache ( key 是 beanName,value 是 beanFactory 产生出来的 object 也是咱们正要获取的 bean ) 这个 Map 中是否存在这个 beanName 这个 bean

  3. 若是存在的话、就直接返回、若是不存在的话、那就 doGetObjectFromFactoryBean ,从这个方法中使用 FactoryBean#getObject 产生 bean

  4. 其实下面这段代码确实让人看不懂哦

    Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
    if (alreadyThere != null) { // 已经存放到 缓存中了、后续的操做就不须要了 object = alreadyThere; } 复制代码
  5. 而后咱们看到 beforeSingletonCreation 这个方法、就是上面 getSingletonisSingletonCurrentlyInCreation 判断一个 bean 是否处于正在建立中

    protected void beforeSingletonCreation(String beanName) {
     if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {  throw new BeanCurrentlyInCreationException(beanName);  }  }  public boolean isSingletonCurrentlyInCreation(String beanName) {  return this.singletonsCurrentlyInCreation.contains(beanName);  } 复制代码
  6. 而后又调用到 postProcessObjectFromFactoryBean 方法、最终回调的就是咱们经常使用的一个接口 BeanPostProcessor

  7. 最好调用 afterSingletonCreation(beanName) 方法、将其从 正在建立中的 bean 的集合中移除、最后的最后、将其加入到 factoryBeanObjectCache 集合中

今天咱们就先分析到这里、后续的话咱们在后面的文章继续探讨。今天咱们大体分析了 getBean 里面的这三个方法

image-20200530152154079
image-20200530152154079

总结

  • 根据参数中的 name 找出对应的 beanName、不管这个 name 是别名或者是一个 factoryBean 的 beanName
  • 查看缓存中是否包含这个 beanName 对象
    • 先从一级缓存 singletonObjects 中看看有没有
    • 而后从二级缓存 earlySingletonObjects
    • 都没有的话再从三级缓存 singletonFactories 中看看有没有
  • 若是缓存中有 bean、那么咱们仍是须要处理一下这个 bean
    • 若是 Spring 缓存中返回的 bean 是 factoryBean、而用户也想要的是一个 beanFactory (参数 name 中的前缀是 & )、那么咱们直接返回
    • 若是 Spring 缓存中返回的 bean 是普通的 bean、而用户也想要的是一个普通的 bean 、那么就直接返回
    • 若是 Spring 缓存中返回的 bean 是一个 factoryBean、而用户想要的是一个普通的 bean 、那么咱们就要从 factoryBean 中获取这个 bean
    • 而从 factoryBean 中获取这个 bean的过程当中、须要调用到前置处理、后置处理和咱们经常使用的接口回调 BeanPostProcessor

上面的三个方法大体流程就是这样、但愿对各位有帮助

有兴趣进入群聊、一块儿交流一块儿划水

image-20200530170736820
image-20200530170736820
此次必定?
此次必定?
相关文章
相关标签/搜索