论 spring 的三级缓存

news2024/11/16 5:31:50

论 spring 的三级缓存

预备知识

bean的生命周期

bean 的生命周期

为何会出现循环引用

这个问题只是出现在spring 容器的机制中,其实我们代码中很简单就解决了。

假设A 对象里需要注入一个B属性,B 对象里面需要注入一个A 属性。根据Bean 的生命周期,先实例化A 的实例,然后进行A属性的填充,这时就需要一个B的对象,在通过 beanFactory.getBean(B) 进行B对象的获取,同样,先进行B的实例化,之后进行B中A属性的复制,也会去调用beanFactory.getBean(A),进行获取,但是A变量还没有创建完成,singletonObjects变量池中没有,所以就行程了循环引用,为了解决这个问题就是A实例化之后,想找一个变量进行存储,后面有变量的创建需要用到她的话,直接使用这个变量的对象,earlySingletonObjects ,这样就解决了循环依赖的问题。

伪代码如下:

//伪代码就是这个意思 
static class A {
        private B b;

        public void setB(B b) {
            this.b = b;
        }
    }
    static class B {
        private A a;

        public void setA(A a) {
            this.a = a;
        }
    }

    public static void main(String[] args) {
        A earlySingletonObjects = new A();
        B b;
        {
            b = new B();
            b.setA(earlySingletonObjects);
        }
        earlySingletonObjects.setB(b);
        A a = earlySingletonObjects;
        // TODO:  use a use b 
    }

不用三级缓存行不行

首先提出一个观点,不用三级缓存,使用两级缓存行不行,答案是肯定的,没问题,spring 为什么要使用三个变量池呢,我想是这样分类更明确。

说道分类明确,大家不禁有疑问,为什么这么说呢,是因为 假设 A 和B 之间进行循环引用,并且我们对A 进行了切面增强逻辑,如果我们只是将A的实例对象提前暴露出去是错误的,因为B中我们真正想使用的是A增强后的对象。好了,问题说清楚了,那用两个变量行不行,当然行了,创建A 的时候我们在进行属性赋值的时候,直接创建一个包含A 的代理对象,并将代理对象放入earlySingletonObjects 提前暴露变量池中,不就ok了吗,等到B使用的时候直接拿到A的代理对象,没有任何问题。所以我个人认为spring 在弄出一个singletonFactories 单纯就是解耦。

三级缓存,不 ,是三个变量

分别存储在 这三个变量当中.

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三个对象的赋值过程

过程详解

代码如下:

  1. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {	
    if (earlySingletonExposure) {
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
}
  1. org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
		Object sharedInstance = getSingleton(beanName);
}
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;
	}
  1. org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    synchronized (this.singletonObjects) {
        singletonObject = singletonFactory.getObject();
        newSingleton = true;
        if (newSingleton) {
            //该方法
            addSingleton(beanName, singletonObject);
        }
        return singletonObject;
     }
}

protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

扩展出aop 代理对象如何创建

首先我们开启aspectj的注解会给容器注册一个 BeanPostProcessor class name 为 AnnotationAwareAspectJAutoProxyCreator

@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

AnnotationAwareAspectJAutoProxyCreator 的继承结构如下:

那么在什么时机去创建代理呢? 有两个时机,第一个是允许提前暴露的话,在实例化好目标对象之后,提前暴露给singletonFactories 当中时,使用getEarlyBeanReference 方法进行动态代理的替换

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {
    	BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
		
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
            //使用getEarlyBeanReference 方法进行动态代理的替换
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		return exposedObject;
	}

getEarlyBeanReference 所做的内容,会调用所有的SmartInstantiationAwareBeanPostProcessor 的getEarlyBeanReference 方法

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

此时创建代理对象

public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

剩下得流程该对目标对象属性赋值就赋值,该初始化初始化。一切照旧,只不过我们提前暴露的是 代理对象.

而after 做的是将为提前暴露的对象在代理一次

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

关于spring aop的源码分析,可以参看:

<<spring技术内幕>>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/505958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【原创】运维工程师涨薪计划,chatGPT帮你做规划

文章目录 1、运维工程师怎么涨薪呢&#xff1f;a&#xff09;加大深度b&#xff09;加大广度 2、运维工程师何处去呢&#xff1f;3、chatGPT告诉你3年、5年、10年运维和开发的现状&#xff1b;有运维经验的工程师&#xff0c;搞开发好吗薪资会有显著提升吗以数据证明&#xff0…

校园食堂明厨亮灶AI分析系统 yolov5

校园食堂明厨亮灶AI分析系统通过yolov5网络模型技术&#xff0c;校园食堂明厨亮灶监控分析模型算法针对校园餐厅后厨不按要求戴口罩、不穿厨师帽、陌生人员进入后厨、厨师不穿厨师服、上班时间玩手机、老鼠识别等行为自动识别抓拍告警。Yolo算法&#xff0c;其全称是You Only L…

电脑缺少msvcp140.dll怎么办,缺少msvcp140一键修复方法

电脑缺少msvcp140.dll怎么办&#xff1f;这个问题相信不少小伙伴都遇到过&#xff0c;msvcp140.dll文件是很多软件跟游戏运行必须用到的文件&#xff0c;如果丢失或者损坏&#xff0c;很多软件都会无法打开运行。其实知道知道方法&#xff0c;修复起来其实也不会很难&#xff0…

ModelViewSet额外路由参数 与 ADMIN事件触发

ModelViewSet额外路由参数 用了DefaultRouter&#xff0c;那么在URL匹配时带路径参数&#xff0c;只有update、delete、retrieve能带&#xff0c;关键是匹配pk还是写死的&#xff0c;我们需要对任意参数&#xff0c;同时还能修改方法命名。 咋搞&#xff1f;很简单。比如我们…

JS案例:前端Iframe及Worker通信解决思路

目录 前言 Iframe通信 Worker通信 实现思路 实现过程 MessageCenter类 IPC类 Server类 Client类 PeerToPeer 功能演示 基础功能 父子通信 兄弟通信 父子兄弟通信 其他功能 函数调用 索引标识 卸载页面 重置页面 批量执行 批量操作 总结 前言 在前端开发…

虚拟机VMware Tools安装步骤

Vmware tools是虚拟机中一款超级增强工具&#xff0c;可以让我们更加方便使用虚拟机&#xff0c;能实现主机与虚拟机之间的文件共享&#xff0c;这篇文章主要介绍了虚拟机VMware Tools安装步骤,需要的朋友可以参考下 本人安装VMware Tools 的需求是 在Linux虚拟机和我的Windo…

Linux学习记录——이십삼 进程信号(2)

文章目录 1、可重入函数2、volatile关键字3、如何理解编译器的优化4、SIGCHLD信号 1、可重入函数 两个执行流都执行一个函数时&#xff0c;这个函数就被重入了。比如同一个函数insert&#xff0c;在main中执行时&#xff0c;这个进程时间片到了&#xff0c;嵌入了内核&#xf…

博客系统 —— Servlet 实现(前后端分离)(详细+整体代码+效果图)

目录 一、项目效果展示 二、创建 Servlet 项目 三、编写数据库的操作代码 1、创建数据库/表结构&#xff08;数据库设计&#xff09;&#xff08;Model&#xff09; 2、封装数据库操作&#xff08;Model&#xff09; &#xff08;1&#xff09;先创建 DBUtil 封装数据库连…

Etsy总是不出单怎么办?出单后怎么操作?

如果您在Etsy上总是无法出单&#xff0c;可以尝试以下几个步骤&#xff1a; 1、检查您的商品列表是否符合Etsy的要求&#xff0c;包括商品描述、价格、运费等信息是否准确无误。 2、确认您的账户信息是否完整&#xff0c;包括银行账户、信用卡信息等。 3、检查您的店铺设置是否…

socket API的使用+client/server代码演示+封装socket模块

前言&#xff1a;本章旨在讲解常见的socket API及其使用练习&#xff0c;尾部有封装好的&#xff08;socket相关的一些系统函数加上错误处理代码包装成新的函数&#xff09;模块wrap.c 目录 一、socket模型创建流程图 1&#xff09;socket函数 2&#xff09;bind函数 3&am…

Altium软件中相同模块布局布线的方法

文章目录 1、原理图设计1、绘制xxx.SchDoc&#xff0c;并设置port。具体方法&#xff1a;Place→Port。2、新建顶层原理图&#xff1a;可以命名为xxx_TOP3、repeat 原理图&#xff0c;将这里从XXX_SingleDut 改为 Repeat(S,1,12)4、以总线的方式出线&#xff0c;如下&#xff1…

网络安全CVE 漏洞分析及复现

漏洞详情 Shiro 在路径控制的时候&#xff0c;未能对传入的 url 编码进行 decode 解码&#xff0c;导致攻击者可以绕过过滤器&#xff0c;访问被过滤的路径。 漏洞影响版本 Shiro 1.0.0-incubating 对应 Maven Repo 里面也有 【一一帮助安全学习&#xff0c;所有资源获取一一…

NetBackup 10.2 新功能介绍:PostgreSQL 和 MySQL 自动化恢复达成

NetBackup 10.2 新功能介绍&#xff1a;PostgreSQL 和 MySQL 自动化恢复达成 原文来自&#xff1a;VERITAS 中文社区 2023-04-27 在执行恢复任务时&#xff0c;手动提取、更新数据库和实例并将其附加到 PostgreSQL 和 MySQL 是常规操作。而在最新的 NetBackup 10.2 版本中&am…

数据可视化工具 - ECharts以及柱状图的编写

1 快速上手 引入echarts 插件文件到html页面中 <head><meta charset"utf-8"/><title>ECharts</title><!-- step1 引入刚刚下载的 ECharts 文件 --><script src"./echarts.js"></script> </head>准备一个…

apc-service-bus项目Docker镜像发布

apc-service-bus项目Docker镜像发布 1. 提交代码到Gitee代码仓&#xff0c;通过建木将项目打包到服务器 1.1 可直接打开访问建木&#xff0c;无有不熟悉建木发布流程的请咨询其他同事或者自行研究 建木地址&#xff1a;http://10.11.148.21/ 1.2 找到bus的开发环境部署执行…

神经网络全连接层数学推导

全连接层分析 对于神经网络为什么都能产生很好的效果虽然其是一个黑盒&#xff0c;但是我们也可以对其中的一些数学推导有一定的了解。 数学背景 目标函数为 f ∣ ∣ m a x ( X W , 0 ) − Y ∣ ∣ F 2 &#xff0c;求 ∂ f ∂ W , ∂ f ∂ X , ∂ f ∂ Y 目标函数为f ||ma…

SpringBoot项目如何打包成exe应用程序

准备 准备工作&#xff1a; 一个jar包&#xff0c;没有bug能正常启动的jar包 exe4j&#xff0c;一个将jar转换成exe的工具 链接: https://pan.baidu.com/s/1m1qA31Z8MEcWWkp9qe8AiA 提取码: f1wt inno setup&#xff0c;一个将依赖和exe一起打成一个安装程序的工具 链接:…

设计模式——代理模式(静态代理、JDK动态代理、CGLIB动态代理)

是什么&#xff1f; 如果因为某些原因访问对象不适合&#xff0c;或者不能直接引用目标对象&#xff0c;这个时候就需要给该对象提供一个代理以控制对该对象的访问&#xff0c;代理对象作为访问对象和目标对象之间的中介&#xff1b; Java中的代理按照代理类生成时机不同又分…

婴儿摇篮语音播放芯片,高品质MP3音乐播放芯片,WT2003H

婴儿摇篮是一种用于帮助婴儿入睡的设备。传统的婴儿摇篮通常只是简单的摇晃&#xff0c;但是带有语音播报芯片的婴儿摇篮则可以更好地模拟妈妈的声音&#xff0c;从而更有效地帮助婴儿入睡。 如果您正在寻找高品质音乐摇篮方案&#xff0c;那么WT2003H语音播放芯片&#xff0c…

5月7日 2H55min|5月8日8H50min|时间轴复盘|14:00~14:30

5月8日 todo list list4 40min ✅ |实际上用了50+50 list6 40min ✅ |实际上用了30+60 阅读+听力连做 100min ✅ 口语 day01 ✅ 口语 day02 口语 day03