Spring FactoryBean 缓存

简介: 读完这篇文章你将会收获到• DisposableBean 的 destroy 执行• Spring 如何缓存 FactoryBean 产生的单例 bean• 如何解决 FctoryBean 的 getObject 的循环依赖

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

  • DisposableBeandestroy 执行
  • Spring 如何缓存 FactoryBean 产生的单例 bean
  • 如何解决 FctoryBeangetObject 的循环依赖


不多BB , 上图


SingletonBeanRegistry


今天我们来扯一下 SingletonBeanRegistry , 我们向 Spring 容器注册 bean 的时候就是用到这个接口的方法

public interface SingletonBeanRegistry {
   void registerSingleton(String beanName, Object singletonObject);
   @Nullable
   Object getSingleton(String beanName);
   boolean containsSingleton(String beanName);
   String[] getSingletonNames();
   int getSingletonCount();
 // 并发控制 现在的实现都是 使用 第一级缓存的 singletonObjects 对象
   Object getSingletonMutex();
}
复制代码


DefaultSingletonBeanRegistry


我们先看看这个接口的默认实现类 DefaultSingletonBeanRegistry 我们前几篇文章说的三级缓存就是在这里定义的

/**
 * 第一级缓存
 */
 Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
 * 第三级缓存
 */
 Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
 * 第二级缓存
 **/
 Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
 * 只要是加入到三级缓存中到 beanName 都会被注册到这个 set 无论是第三级缓存或者是第一级缓存
 */
 Set<String> registeredSingletons = new LinkedHashSet<>(256);
/**
 * 正在处于一个创建状态的 bean、存放的是 beanName
 */
 Set<String> singletonsCurrentlyInCreation =
      Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
 * 不用在创建的时候检查循环依赖的 beanName 名称
 */
 Set<String> inCreationCheckExclusions =
      Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
 * 收集一些并不影响主流程的异常,可用于后续再次抛出的时候做一些关联,或者只是收集而不抛出
 */
@Nullable
 Set<Exception> suppressedExceptions;
/**
 * 表明是否正处于一个正在销毁 singleton 的过程
 */
 boolean singletonsCurrentlyInDestruction = false;
/**
 * beanName:需要执行destroyMethod 的bean
 */
 Map<String, Object> disposableBeans = new LinkedHashMap<>();
/**
 * * key: 外部的 beanName  
  * value 外部 bean 依赖的一些内部 bean
 */
 Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/**
 * key bean name
 * value 所有依赖 key的 bean
 */
 Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/**
 * key bean name
 * value 这个key 所依赖的bean
 */
 Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
复制代码

上面除了三级缓存以外,还有其他一些属性定义


getSingletonMutex


我们再来看看其如何实现 SingletonBeanRegistry 接口的方法

@Override
public final Object getSingletonMutex() {
   return this.singletonObjects;
}
复制代码

返回第一级缓存这个 Map 作为同步锁的对象,子类需要使用 synchronized 的时候就要获取这个同步锁对象


registerSingleton


@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
   Assert.notNull(beanName, "Bean name must not be null");
   Assert.notNull(singletonObject, "Singleton object must not be null");
   synchronized (this.singletonObjects) {
      Object oldObject = this.singletonObjects.get(beanName);
      if (oldObject != null) {
         throw new IllegalStateException("xxxxx");
      }
      addSingleton(beanName, singletonObject);
   }
}
复制代码
protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
     // 加入到第一级缓存
      this.singletonObjects.put(beanName, singletonObject);
     // 从第三级缓存中移除
      this.singletonFactories.remove(beanName);
     // 从第二级缓存中移除
      this.earlySingletonObjects.remove(beanName);
     // 注册这个beanName
      this.registeredSingletons.add(beanName);
   }
}
复制代码

先是对参数的检验、然后使用同步锁,再判断该 beanName 是否已经在第一级缓存中、如果已经存在了、则抛出异常,不管在缓存中的 bean 是否跟参数中的 singletonObject 是同一个对象

然后加入到第一级缓存中并注册这个 beanName、然后从第二级第三级缓存中移除这个 beanName


getSingleton


@Override
@Nullable
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;
}
复制代码

上面这个方法我们已经见过了很多次 , 分别从第一级而后第二级、再到第三级获取 bean、如果从第三级的话、那么就将其升级到第二级并从第三级移除。


其他


下面这三个方法就比较简单了、不做介绍了、相信大家都能看一眼就知道干啥了

@Override
public boolean containsSingleton(String beanName) {
   return this.singletonObjects.containsKey(beanName);
}
@Override
public String[] getSingletonNames() {
   synchronized (this.singletonObjects) {
      return StringUtils.toStringArray(this.registeredSingletons);
   }
}
@Override
public int getSingletonCount() {
   synchronized (this.singletonObjects) {
      return this.registeredSingletons.size();
   }
}
复制代码


我们再来看一个往第三级缓存中存放 ObjectFactory 的实现

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) {
         this.singletonFactories.put(beanName, singletonFactory);
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      }
   }
}
复制代码


我们再来看看注册 DisposableBean 的方法吧

public void registerDisposableBean(String beanName, DisposableBean bean) {
   synchronized (this.disposableBeans) {
      this.disposableBeans.put(beanName, bean);
   }
}
复制代码


而执行 destroy 的话内容比较长

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
    Set<String> dependencies;
   synchronized (this.dependentBeanMap) {
       // 找出所有依赖这个 beanName 的其他 bean
       dependencies = this.dependentBeanMap.remove(beanName);
   }
   if (dependencies != null) {
      for (String dependentBeanName : dependencies) {
        // 最终还是回调到这个方法
         destroySingleton(dependentBeanName);
      }
   }
 // 如果 DisposableBean 不为null
   if (bean != null) {
      try {
         bean.destroy();
      } catch (Throwable ex) {
      }
   }
 // 处理内部 bean 了
   Set<String> containedBeans;
   synchronized (this.containedBeanMap) {
  // 找出这个 beanName 的所有内部 bean
     containedBeans = this.containedBeanMap.remove(beanName);
   }
   if (containedBeans != null) {
      for (String containedBeanName : containedBeans) {
         destroySingleton(containedBeanName);
      }
   }
  // dependentBeanMap key 为被依赖者、value 为依赖 key 的 bean
  // 这一步的操作就是因为 beanName 可能存在 别人的 value 中、这个时候我们也要去清理掉
  // 第一步的时候已经清除了 key 为 beanName 的情况
   synchronized (this.dependentBeanMap) {
      for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext(); ) {
         Map.Entry<String, Set<String>> entry = it.next();
         Set<String> dependenciesToClean = entry.getValue();
         dependenciesToClean.remove(beanName);
         if (dependenciesToClean.isEmpty()) {
            it.remove();
         }
      }
   }
  // dependenciesForBeanMap key 为依赖者,value 为 key 依赖的 bean 集合
   this.dependenciesForBeanMap.remove(beanName);
}
复制代码


FactoryBeanRegistrySupport


我们在来看看这个类,这个类是干啥的

当我们向 Spring 注册的 beanFactoryBean 的话、那么这个 FactoryBean 当然是存放在三级缓存中啦、但是这个 FactoryBean 产生的 bean 也得有个地方缓存起来吧(如果是个单例的话,是吧)

/**
 * beanName: bean(factory bean 创建出来的)
 * Cache of singleton objects created by FactoryBeans: FactoryBean name to object.
 */
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
复制代码

我们这里介绍一个上几篇文章说过的一个方法、但是其中的妙处、我现在算是看懂了

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
      // 为单例模式且 beanName 已经注册了在 Spring 中
   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)) {
                     // Temporarily return non-post-processed object, not storing it yet..
                     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 = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
   // 为空则从 factory bean 中获取对象
   object = doGetObjectFromFactoryBean(factory, beanName);
   Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
   if (alreadyThere != null) {
     // 已经存放到 缓存中了、后续的操作就不需要了
     object = alreadyThere;
   } 
复制代码


我上一步已经判断了 factoryBeanObjectCache 里面没有这个 beanName 了,而 doGetObjectFromFactoryBean 这个方法只是单纯的去调用 FactoryBean 里面的 getObject 方法、并无其他操作,那么为啥 alreadyThere 它会有不为 null 的情况呢 ?


我们先设定、FactoryBeanbeanNamebeanA 吧,还有一个普通的 beanB、它是依赖 beanA 的。


那么假如我在 beanAgetObject 的方法里面调用 getBean 方法获取 beanB , 这个时候就构成了一个循环依赖,当创建好 beanB 的时候、进行属性注入,发现要 beanA、这个时候就会继续走上面的流程、也就是 alreadyThere == null 的情况、这个时候会将 beanA 放置到 factoryBeanObjectCache 中、最终创建好了 beanB , 返回到 doGetObjectFromFactoryBean 这里的方法、这个时候就会产生了两个 beanA (如果你正常的在 getObjectnew 某个对象的话) , 是不是就出现了 alreadyThere 不为 null 的情况了

来个 demo 看看吧

public class CatFactoryBean implements FactoryBean<Cat>, BeanFactoryAware {
   private BeanFactory beanFactory;
   @Override
   public Cat getObject() throws Exception {
      beanFactory.getBean("chicken");
      return new Cat();
   }
   @Override
   public Class<?> getObjectType() {
      return Cat.class;
   }
   @Override
   public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.beanFactory = beanFactory;
   }
}
复制代码
public class Chicken {
   private Cat cat;
   public Chicken() {
   }
   public void destroyMethod() {
      System.out.println("destroy method");
   }
   public void setCat(Cat cat) {
      this.cat = cat;
   }
}
复制代码
public static void main(String[] args) {
      Resource resource = new ClassPathResource("coderLi.xml");
      DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
      XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
      xmlBeanDefinitionReader.loadBeanDefinitions(resource);
      Object cat = defaultListableBeanFactory.getBean("cat");
      defaultListableBeanFactory.destroySingletons();
   }
复制代码
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean class="com.demo.data.Chicken" id="chicken" destroy-method="destroyMethod">
      <property name="cat" ref="cat"/>
   </bean>    
   <bean class="com.demo.data.CatFactoryBean" id="cat"/>
</beans>
复制代码


目录
相关文章
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
27天前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
2月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
157 24
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
2月前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
31 1
|
27天前
|
XML 缓存 Java
Spring FactoryBean 的常见使用场景总结
FactoryBean 是 Spring 框架中的一个重要接口,用于自定义 Bean 的创建逻辑。常见使用场景包括: 1. **复杂 Bean 的创建**:如数据源配置。 2. **延迟实例化**:按需创建资源密集型对象。 3. **动态代理**:为 Bean 创建 AOP 代理。 4. **自定义配置**:根据特定配置创建 Bean。 5. **第三方库集成**:利用 FactoryBean 封装外部库的创建过程。
|
3月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
733 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
3月前
|
缓存 Java Spring
Spring缓存实践指南:从入门到精通的全方位攻略!
【8月更文挑战第31天】在现代Web应用开发中,性能优化至关重要。Spring框架提供的缓存机制可以帮助开发者轻松实现数据缓存,提升应用响应速度并减少服务器负载。通过简单的配置和注解,如`@Cacheable`、`@CachePut`和`@CacheEvict`,可以将缓存功能无缝集成到Spring应用中。例如,在配置文件中启用缓存支持并通过`@Cacheable`注解标记方法即可实现缓存。此外,合理设计缓存策略也很重要,需考虑数据变动频率及缓存大小等因素。总之,Spring缓存机制为提升应用性能提供了一种简便快捷的方式。
45 0
|
3月前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
59 0
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
3月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决