Appearance
Spring
Bean线程安全
Spring 框架中的单例 Bean 是线程安全的吗
并不是线程安全的,Spring框架中的单例Bean本身对于线程安全性并没有提供保证。
单例Bean意味着在Spring的应用上下文中,每个定义的Bean只有一个实例。但是,Bean的线程安全性取决于Bean的实现和使用方式。
- 状态无关的Bean:如果Bean是无状态的(即,它们不维护任何字段或状态信息),那么它们通常是线程安全的。例如,一个只有方法,没有成员变量的服务类,或者成员变量都是不可变的或线程安全的。
- 状态相关的Bean:如果Bean包含有状态(即,它们维护了修改的字段或状态信息),那么它们可能不是线程安全的。在并发访问下,如果多个线程同时修改这些状态,可能会导致数据不一致或其他并发问题。
服务器中的代码片段
Spring Bean 并没有可变的状态(比如 Service 类和 Dao 类),所以在某种程度上说 Spring 的单例 bean 是线程安全的。
但如果 Bean 中包含了修改的字段或者状态信息, 多线程环境下会存在线程安全问题。
@Component
public class CounterBean {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}以上的CounterBean就是一个有状态的Bean,多线程环境下对increment方法的调用就会导致线程安全问题。
面试题回答
Spring 框架中的单例 Bean 是线程安全的吗
- 不是线程安全的
- Spring 的 Bean 默认是单例的,是否是线程安全有两种情况
- 在spring的bean的中都是注入无状态的对象,没有线程安全问题
- 如果在bean中定义了可修改的成员变量,是要考线程安全问题的
- 可以使用多例或者加锁来解决
AOP
什么是 AOP,你们项目中有没有使用到 AOP
回答这道题目,一般建议围绕首先自己对于 AOP的理解概念,然后结合实际场景题,包括自己项目中是如何使用到 AOP ,然后讲述自己对于它的理解。
AOP称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个 可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的调合度,同时提高了系统的可维护性,
常见的AOP 使用场景是:
- 记录操作日志
- 缓存处理
- Spring中内置的事务处理
日志记录
记录操作日志思路
- 获取请求的用户名、请求方式、访问地址、模块名称、登陆IP、操作时间,记录到数据库的日志表中。
日志的实际记入方式可用说一下,然后日志在项目中是如何用到的(建议说一下蘑菇博客上面记录日志的方式)
事务实现

感觉AOP 这里的回答建议还是要和反向代理一起说一下,具体实现方式当然也可用说,到时候随机应变了。
面试题回答

事务失效
事务管理通过声明式事务和编程式事务来提供对事务的支持。在某些场景下,Spring事务可能会失效,导致事务不按预期执行。
这些场景主要包括:
- 方法非公开(Non-public Method)
- Spring的声明式事务管理是通过动态代理实现的。
- 如果一个类中被
@Transactional标注的方法不是public的,那么Spring AOP将无法创建有效的代理对象,导致事务管理不生效。
- 自调用(Self-invocation)
- 当在同一个类中的一个方法直接调用另一个带有
@Transactional注解的方法时,事务是不会被触发的。 - 这是因为动态代理的限制,事务的应用需要通过代理对象来调用目标方法。自调用场景绕过了代理对象,直接在目标对象内部调用,因此事务不生效。
- 异常处理不当
- Spring事务默认只对
RuntimeException(运行时异常)和Error进行回滚,对于检查型异常(Exception的其他子类)不会回滚。 - 如果需要对检查型异常进行回滚,需要在
@Transactional注解的rollbackFor属性中显式声明。
- 数据源未配置事务管理器
- 错误的事务传播行为配置
- 事务方法被内联编译优化
- 不恰当的隔离级别或超时设置
建议回答一下前三点。
视频讲解部分内容:
异常捕获处理

抛出检查异常
非 public 方法导致的事务失效

面试题

Bean的生命周期 🚩
Spring 容器是如何管理和创建 bean 实例
方便调试和解决问题
Bean 的流程 → 代码验证
BeanDefinition

生命周期

自定义了一个类,然后看 Bean 在 Spring 中的一个执行流程(看输出的一个顺序)
面试题回答

循环依赖 🚩


看一下相关的解析:
关于三级缓存解决循环引用的描述,基本思路是对的,但描述过程中的一些细节需要更准确地表达:
- 开始实例化对象A:当Spring容器开始创建Bean A时,如果发现A依赖于B,它会暂停A的创建过程,并开始创建B。
- 放入三级缓存:在这个过程中,Spring容器实际上会将创建A的部分完成的实例(也就是说,这个实例已经分配了内存,但它的依赖还没有完全解决)的工厂放入三级缓存中。这个工厂负责生成A的引用,以便在需要时可以提前暴露出来。
- 创建对象B:在创建B的过程中,如果B需要依赖于A,Spring容器会尝试从缓存中获取A的实例。此时,它会从三级缓存中获取A的工厂,使用这个工厂创建A的早期引用,并将这个引用提升到二级缓存中。
- 完成对象B的创建:有了A的引用后,Spring容器可以继续B的创建过程,并解决其对A的依赖。
- 完成对象A的创建:一旦B被成功创建并设置到A中,Spring容器回到A的创建过程中,完成A的创建并解决其他可能的依赖。
通过三级缓存机制,Spring能够处理循环依赖的情况,允许A和B互相引用而不导致死锁或Bean创建失败。
不过,需要注意的是,Spring的这种三级缓存机制只能解决单例作用域Bean的循环依赖问题,并且主要解决的是Setter注入或字段注入造成的循环依赖。对于构造器注入导致的循环依赖,Spring是无法解决的。
👇
构造方法出现了循环依赖的话,可以使用 @Lazy 进行懒加载

常用注解
Spring框架常见注解(Spring、Springboot、Springmvc)
Spring
- @Componetn、@Controller、@Service、@Repository
- @Autowired
- @Qualifier
- @Scope
- @Configuration
- @ComponentScan
- @Bean
- @Import
- @Aspect、@Before、@After、@Around、@Pointcut

SpringMVC
- @RequestMapping
- @RequestBody
- @RequestParam
- @PathViriable
- @ResponseBody
- @RequestHeader
- @RestController

SpringBoot
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @CompnentScan

你们项目中有用过 SPI 吗,是否自己封装过组件 🚩
有没有自定义过 BeanPost xxx ,具体怎么使用的 🚩
SpringMVC
执行流程
Springmvc的执行流程是这个框架最核心的内容
- 1、视图阶段(老旧JSP等 )
- 2、前后端分离阶段(接口开发,异步)
视图阶段

前后端分离

面试题:
- 1、浏览器发起请求,前端控制器进行处理(调用处理器映射器);
- 2、前端控制器通过处理器映射器会找到具体的处理器 Handler,生成处理器对象以及处理器拦截器(如果有),一起返回给 DispatcherServlet。
- 3、前端控制器调用处理器适配器,适配器调用对应的 处理器 进行处理用户请求
- 4、处理完成后返回 ModelAndView 对象,经过视图解析器处理返回视图,进行渲染后填充前端,进行响应

版本2:
- 1、浏览器发起请求,前端控制器进行处理(调用处理器映射器);
- 2、前端控制器通过处理器映射器会找到具体的处理器 Handler,生成处理器对象以及处理器拦截器(如果有),一起返回给 DispatcherServlet。
- 3、前端控制器调用处理器适配器,适配器调用对应的 处理器 进行处理用户请求
- 4、一般此时 Controller 方法上会添加 @ResponseBody 注解,会进行将返回结构转换为 Json 并响应前端

SpringBoot
自动配置原理 🚩
这道题目待复习一下



2,其中@EnableAutoConfiguration是实现自动化配置的核心注解。
- 该注解通过@Importi注解导入对应的配置选择器。
- 内部就是读取了该项目和该项目引用的ar包的的classpath路径下META-NF/spring.factories文件中的所配置的类的全类名。
- 在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
- 3,条件判断会有像 @ConditionalOnClassi这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的 Bean放入 spring容器中使用.

Mybatis
# 和 { } 替换
动态标签
if、where、set
select、foreach,
执行流程 🚩
Mybatis 执行流程
- 1、理解各个组件的关系
- 2、Sql 的执行过程(参数映射、sql 解析、执行和结果处理)
讲一下 mybatis 的配置文件的 xml 文件的作用有哪些?
- 加载核心配置
- 加载映射文件

执行流程

面试题

延迟加载 🚩
延迟加载操作:需要配置文件中开启(关联对象集合的延迟加载)
实现原理:动态代理,CGLIB

- 1、设置局部的延迟加载
- 2、设置全部的延迟加载

主要看一下这个:

一二级缓存 🚩
二级缓存

注意事项:
- 1、对于缓存数据更新机制,当某一个作用域(一级缓存Session/,二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被clear
- 2,二级缓存需要缓存的数据实现 Serializable 接口
- 3,只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
面试题:

缓存
- 一级缓存(本地缓存)
- 一级缓存是 SqlSession 级别的,也就是说,只有在同一个 SqlSession 中的操作才能共享这个缓存。默认情况下,一级缓存是开启的
- 二级缓存(全局缓存)
- 二级缓存是跨 SqlSession 的,它是基于 namespace 级别的,即同一个 namespace 的不同 SqlSession 可以共享该缓存
- 需要显式开启。可以在 MyBatis 配置文件中全局开启,也可以在 mapper 映射文件中单独配置使用二级缓存
- 当一个 SqlSession 查询完数据并提交或关闭时,数据会从一级缓存转移到二级缓存中。当另一个 SqlSession 执行查询时,会先查看二级缓存,如果缓存中有数据,则直接使用缓存数据;如果没有,才会访问数据库,并将查询结果放入二级缓存
- 一级缓存(本地缓存)