【Java面试】六、Spring框架相关
文章目录
- 1、单例Bean不是线程安全的
- 2、AOP
- 3、Spring中事务的实现
- 4、Spring事务失效的场景
- 4.1 情况一:异常被捕获
- 4.2 情况二:抛出检查异常
- 4.3 注解加在非public方法上
- 5、Bean的生命周期
- 6、Bean的循环引用
- 7、Bean循环引用的解决:Spring三级缓存
- 8、构造方法出现循环依赖
- 9、面试
1、单例Bean不是线程安全的
@Scope注解下,singleton表示Bean在IOC容器中只有一个实例,prototype则表示一个Bean可以有多个实例

但单例Bean不是线程安全的,Demo代码如下:线程1进来count被改成1,线程2进来,count被改成10,线程交替执行,就乱套了,单例Bean UserController就不线程安全

多个用户同时请求一个微服务,每个请求对应一个线程,如果并发的这段代码中,对单例的成员属性修改了,则存在线程安全问题。上面例子中,UserController这个Bean的成员属性count在被并发修改(存在可修改的成员变量),此时,单例的Bean不是线程安全的。相反,UserService的这个Bean,是不可变状态(不涉及其成员属性的修改),因此,UserService的这个单例Bean是线程安全的。
最后,补充一句,/getById/{id},id属于局部变量,更无线程安全问题了。
2、AOP
项目相关:做链路追踪埋点,采集所有接口的数据库层、Redis执行时间等信息,采用了AOP实现。
AOP,面向切面编程,将那些公共的逻辑代码,抽取封装,匹配要切入的点,实现统一加功能的操作。减少了系统的重复代码,提高了可维护性。其使用场景如自己做公共日志采集上报、Spring用来实现事务

3、Spring中事务的实现
Spring中,事务的实现可以编程式,也可以声明式。前者使用TransactionTemplate实现,对业务代码有侵入性。后者使用@Transactional注解。加注解实现事务,底层用的是AOP:

实现如上:切点表达式匹配@Transactional注解,使用环绕通知,执行前,加开启事务的代码,执行后,加提交事务的代码,出现异常,回滚事务。
4、Spring事务失效的场景
4.1 情况一:异常被捕获
如下:不加try-catch,发生异常后,事务回滚。加了try-catch,发生异常后,被catch捕获处理了。@Transactional对应的AOP没有收到异常,就会去提交事务。导致了异常后面的业务代码没执行,就会出现转账账户扣钱了,但被转账的用户余额不变的情况。

4.2 情况二:抛出检查异常
以下,读取的文件不存在,抛出了FileNotFound异常,但事务并不会回滚

因为Spring默认只会回滚非检查异常(RunTimeException),解决办法是,添加属性,指明事务回滚的异常种类,以下写法即只要发生异常就回滚:
@Transactional(rollbackFor=Exception.class)
4.3 注解加在非public方法上

Spring为方法创建代理,AOP添加事务通知的前提是:方法是public的,此时需要改为public方法
5、Bean的生命周期
首先,Spring容器将xml中的<bean>信息封装成一个个BeanDefinition对象(该接口的相关方法如下,可获取全类名、初始化方法、作用域,其中后面步骤的初始化,也是从这儿BeanDefition拿的方法名)

接下来:⇒
STEP1:实例化
- 由BeanDefinition拿到构造方法,通过反射去拿到构造函数来(new Instance)实例化,拿到空对象,即纯净态的Bean
- 当然实例化也可能是实例工厂、静态工厂等
STEP2:属性赋值
- 解析自动装配(DI的体现),可byName、byType、constractor
- 这里当然还有循环依赖的情况
STEP3:初始化
- 调用那些XXXAware的回调方法
- 调用初始化生命周期的回调方法(init-method)
- 如果Bean涉及了AOP,还要为这个Bean创建动态代理
STEP4:销毁
- 在Spring容器关闭的时候调用
- 调用销毁生命周期的回调方法(destroy-method)

注意Bean前置处理器和Bean后置处理器的执行时机,是在Bean的init方法执行前后。一般来说,框架中,Bean创建动态代理,常用后置处理器,即实现BeanPostProcessor。代码验证下:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;@Component
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {public User() {System.out.println("User的构造方法执行了.........");}private String name ;@Value("张三") //依赖注入public void setName(String name) {System.out.println("setName方法执行了.........");}@Overridepublic void setBeanName(String name) {System.out.println("setBeanName方法执行了.........");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("setBeanFactory方法执行了.........");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("setApplicationContext方法执行了........");}@PostConstructpublic void init() {System.out.println("init方法执行了.................");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("afterPropertiesSet方法执行了........");}@PreDestroypublic void destory() {System.out.println("destory方法执行了...............");}}
关于前置处理器和后置处理器接口的实现:指定如果是user的Bean,就打印一句话。前面提到,框架中,Bean创建动态代理,常用后置处理器。下面就体现了模拟框架动态代理,给user这个Bean做cglib动态代理的效果:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("user")) {System.out.println("postProcessBeforeInitialization方法执行了->user对象初始化方法前开始增强....");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("user")) {System.out.println("postProcessAfterInitialization->user对象初始化方法后开始增强....");//cglib代理对象Enhancer enhancer = new Enhancer();//设置需要增强的类enhancer.setSuperclass(bean.getClass());//执行回调方法,增强方法enhancer.setCallback(new InvocationHandler() {@Overridepublic Object invoke(Object o, Method method, Object[] objects) throws Throwable {//执行目标方法return method.invoke(method,objects);}});//创建代理对象return enhancer.create();}return bean;}}
效果:

6、Bean的循环引用
如下,A这个Bean创建时,依赖B这个Bean。而B这个Bean的创建,又依赖A这个Bean,即循环引用。有点死锁的感觉。

由Bean的生命周期可知,刚开始,从Beanfinition获取构造方法,反射拿到半成品的A这个Bean,即纯净态的Bean,此时,没有依赖注入,即b属性为null。
接下来,对A这个Bean进行初始化,需要依赖注入,设置b的属性,此时就需要从IoC容器中拿到B这个Bean。很明显,容器中没有,因此,去创建B这个Bean。
同A,纯净态的B,在实例化时,依赖注入,设置a的属性,发现需要A这个Bean。于是又走到了实例化A这条线路。到此,闭环了,走不出来了。

7、Bean循环引用的解决:Spring三级缓存
Spring框架已经帮开发者解决了大部分的循环引用 ⇒ 三级缓存(三个Map )

三个Map中存的分别是:单例已经走完全部流程的成熟态Bean、存半成品的Bean、对象工厂

一级缓存作用是:限制 bean 在 beanFactory 中只存一份,即实现 singleton scope。 仅靠它解决不了循环引用,还是会从1-6步一直循环:

引入二级缓存后,将原始对象A放入二级缓存,再去找B这个Bean。同理,B的原始对象也会先放入二级缓存。接下来B需要依赖注入A这个Bean时,会去从二级缓存中获取,然后B这个Bean创建成功,存储到一级缓存的单例池中,同时,将B在二级缓存中的半成品删掉。再回到造A的路上,将B从容器中注入到A,A也创建成功,将A在二级缓存中的半成品也删掉。

到此,一般对象之间的循环引用到二级缓存就解决了。但还有个场景,如果主要注入的A对象是个代理对象,则需要三级缓存:

8、构造方法出现循环依赖
Spring三级缓存解决了大部分的循环依赖(set注入时的循环依赖),但如果是构造方法的循环依赖,则需自己处理。如下写法,报错:Is there an unresolvable circular reference?

Bean的生命周期第一步,是通过BeanDefinition获取方法名,反射去拿到构造函数来(new Instance)实例化一个纯净态的Bean,还没到set依赖注入这一步(依赖注入的方式不是set方法,而是构造函数),就发现需要一个B这个Bean,同理B一开始执行构造方法就需要A这个Bean。此时Spring框架的三级缓存不能解决。需要加 @Lazy

9、面试




相关文章:
【Java面试】六、Spring框架相关
文章目录 1、单例Bean不是线程安全的2、AOP3、Spring中事务的实现4、Spring事务失效的场景4.1 情况一:异常被捕获4.2 情况二:抛出检查异常4.3 注解加在非public方法上 5、Bean的生命周期6、Bean的循环引用7、Bean循环引用的解决:Spring三级缓…...
【GIC400】——PLIC,NVIC 和 GIC 中断对比
文章目录 PLIC,NVIC 和 GIC 中断对比中断向量表PLIC中断向量表中断使能中断服务函数NVIC中断向量表中断使能中断服务函数GIC中断向量表系列文章 【ARMv7-A】——异常与中断 【ARMv7-A】——异常中断处理概述...
17.Redis之主从复制
1.主从复制是怎么回事? 分布式系统, 涉及到一个非常关键的问题: 单点问题 单点问题:如果某个服务器程序, 只有一个节点(只搞一个物理服务器, 来部署这个服务器程序) 1.可用性问题,如果这个机器挂了,意味着服务就中断了~ 2.性能/支持的并发量也是比较有限…...
计算机类专业应该怎么选学校和方向?优先选这些!
👆点击关注 获取更多编程干货👆 高考季临近,不少有意向报考计算机专业的同学在为院校和细分专业的选择而苦恼,以下是一些建议,希望能帮到大家! 01 选校建议 在选择计算机科学(CS)…...
Amazon云计算AWS(二)
目录 三、简单存储服务S3(一)S3的基本概念和操作(二)S3的数据一致性模型(三)S3的安全措施 四、非关系型数据库服务SimpleDB和DynamoDB(一)非关系型数据库与传统关系数据库的比较&…...
实战
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 实战一:大乐透号码生成器 使用Random模块模拟大乐透号码生成器。选号规则为:前区在1~35的范围内随机产生不重复的…...
【C++】vector模拟实现
🔥个人主页: Forcible Bug Maker 🔥专栏: STL || C 目录 前言🔥vector需要实现的接口函数🔥vector的模拟实现swap交换默认成员函数迭代器接口reserve和resizesize和capacityoperator[ ]下标获取push_back和…...
生成随机图片
package com.zhuguohui.app.lib.tools;/*** Created by zhuguohui* Date: 2024/6/1* Time: 13:39* Desc:获取随机图片*/ public class RandomImage {// static final String url "https://picsum.photos/%d/%d?random%d";static final String url "https://…...
回溯算法常见思路
回溯问题 回溯法,一般可以解决如下几种问题: 组合问题:N个数里面按一定规则找出k个数的集合切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集排列问题:N个数…...
AR眼镜定制开发_在AR眼镜中实现ChatGPT功能
AR眼镜定制方案中,需要考虑到强大的算力、轻巧的设计和更长的续航时间等基本要求。然而,AR眼镜的设计方案不仅仅需要在硬件和显示技术方面取得突破,还要在用户体验方面有所进展。 过去,由于造价较高,AR眼镜的普及和商业…...
手写防抖debounce
手写防抖debounce 应用场景 当需要在事件频繁触发时,只执行最后一次操作,可以使用防抖函数来控制函数的执行频率,比如窗口resize事件和输入框input事件; 这段代码定义了一个名为 debounce 的函数,它接收两个参数:fn…...
anaconda pycharm jupter分别是
Anaconda Anaconda是一个面向数据科学的Python发行版,它包含了Python解释器、conda包管理器、以及大量的科学计算和数据分析库。Anaconda的主要功能是提供一个易于管理的环境,用于安装、运行和更新Python包,同时支持创建和切换不同的Python环…...
【JMeter接口自动化】第3讲 Jmeter语言及外观配置
Jmeter语言配置 方法一:暂时生效,下次打开JMeter还会恢复默认配置 Jmeter安装后,默认语言是英文,可以在“选项”——“选择语音”中更改 方法二,修改配置文件,永久生效 修改jmeter.properties文件 Jmete…...
浅谈云原生安全
一、云原生安全的层级概念 "4C" Code-Container-Cluster-Cloud 二、云原生各个层级的安全实践有哪些? 1、针对于Cloud针对的是公有云层面,其实就一点 1、使用主账号子角色,赋予最小权限原则进行资源管理。 2、对于Cluster 1、从C…...
[线程与网络] 网络编程与通信原理(五): 深入理解网络层IP协议与数据链路层以太网协议
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 🧀Java …...
【Python】超时请求或计算的处理
超时机制 一般应用于处理阻塞问题 场景: 复杂度较大的计算(解析)某个数值、加解密计算等请求中遇到阻塞,避免长时间等待网络波动,避免长时间请求,浪费时间 1. requests 请求超时机制 reqeusts 依赖中的…...
前端 JS 经典:LRU 缓存算法
前言:什么是 LRU 呢,单词全拼 Least Recently Used,意思是最久未使用。这个算法是做缓存用的,比如,你要缓存一组数据,你要划分缓存块出来,因为不可能每个数据都做缓存,那么划出来的这…...
Python3 笔记:ljust、rjust 和 center
1、ljust() 方法返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串。 ljust(self, width, fillchar , /) width -- 指定字符串长度。 fillchar -- 填充字符,默认为空格。 str1 sentence str2 word…...
恒压频比开环控制系统Matlab/Simulink仿真分析(SPWM控制方式)
介绍恒压频比的开环控制方法驱动永磁同步电机的转动,首先分析恒压频比的控制原理,然后在Matlab/Simulink中进行永磁同步电机恒压频比开环控制系统的仿真分析,最后将Simulink中的恒压频比控制算法生成代码加载到实际工程中进行工程实现。 一、…...
chatgpt4和文心一言的简单对比
功能1. 写一个ebpf程序监控udp延迟 文心一言 答案 编写一个完整的eBPF程序来监控UDP延迟涉及多个步骤,包括编写eBPF C代码、编译、加载到内核,以及用户空间程序来读取eBPF map中的数据。以下是一个简化的示例框架,展示如何使用eBPF来监控U…...
终极指南:如何用ComfyUI-VideoHelperSuite快速构建专业视频工作流
终极指南:如何用ComfyUI-VideoHelperSuite快速构建专业视频工作流 【免费下载链接】ComfyUI-VideoHelperSuite Nodes related to video workflows 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-VideoHelperSuite 想要在ComfyUI中轻松处理视频内容&a…...
打卡信奥刷题(3064)用C++实现信奥题 P6871 [COCI 2013/2014 #6] HASH
P6871 [COCI 2013/2014 #6] HASH 题目背景 Mirko 正在研究一个哈希函数。 题目描述 此哈希函数如此定义: f(NULL)0f(\rm{NULL})0f(NULL)0f(aisi)((f(si)33)xorord(ai))modMODf(a_is_i)((f(s_i)\times33)\operatorname{xor}\ \operatorname{ord}(a_i))\bmod …...
Jimeng LoRA多版本对比指南:动态热切换,高效测试不同Epoch生成效果
Jimeng LoRA多版本对比指南:动态热切换,高效测试不同Epoch生成效果 1. 项目背景与核心价值 在AI绘画领域,LoRA(Low-Rank Adaptation)模型已经成为风格定制的重要工具。但训练过程中一个常见痛点是如何高效评估不同训…...
重新定义内容采集:抖音下载器的架构哲学与实践路径
重新定义内容采集:抖音下载器的架构哲学与实践路径 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support.…...
别再傻傻分不清了!一文搞懂前置机、跳板机、堡垒机的区别与实战选型
安全运维三剑客:前置机、跳板机与堡垒机的本质差异与选型策略 当你第一次听到"前置机"、"跳板机"和"堡垒机"这三个术语时,是否也感到一头雾水?这三种设备在安全运维领域各司其职,却又常常被混淆使用…...
MySQL 大事务刷binlog cache引发的DML阻塞问题解析
1. 从阿里云监控案例说起:DML阻塞的诡异现象 上周排查一个线上问题,阿里云监控突然报警显示数据库响应时间飙升。打开SQL洞察一看,发现特别诡异的现象:同一时间点,有的UPDATE语句执行耗时2秒,有的却卡了200…...
构建自动化工作流:cv_unet_image-colorization与GitHub Actions集成实现CI/CD
构建自动化工作流:cv_unet_image-colorization与GitHub Actions集成实现CI/CD 每次给老照片上色,你是不是都得手动跑一遍代码?本地环境配置、依赖安装、模型加载,一套流程下来,十几分钟就过去了。要是团队里好几个人都…...
OpenClaw+Phi-3-vision-128k-instruct自动化测试:保证多模态任务稳定性的3种方法
OpenClawPhi-3-vision-128k-instruct自动化测试:保证多模态任务稳定性的3种方法 1. 为什么需要为多模态AI构建自动化测试体系 上周我尝试用OpenClawPhi-3-vision组合处理一批产品截图时,遇到了典型的多模态任务稳定性问题:模型有时会漏掉图…...
OpenClaw多模型切换指南:Qwen3-4B与本地LLM混合调用
OpenClaw多模型切换指南:Qwen3-4B与本地LLM混合调用 1. 为什么需要多模型混合调用 去年冬天,当我第一次尝试用OpenClaw自动化处理技术文档时,发现一个尴尬的现象:用Qwen3-4B生成代码示例效果很好,但让它润色一段产品…...
深入Minoca OS内核架构:模块化设计与驱动模型解析
深入Minoca OS内核架构:模块化设计与驱动模型解析 【免费下载链接】os Minoca operating system 项目地址: https://gitcode.com/gh_mirrors/os1/os Minoca OS作为一款轻量级操作系统,其内核架构采用高度模块化设计,结合灵活的驱动模型…...
