Skip to content

面试煎熬成蛋_框架

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、操作时间,记录到数据库的日志表中。

日志的实际记入方式可用说一下,然后日志在项目中是如何用到的(建议说一下蘑菇博客上面记录日志的方式)

事务实现

image.png

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

面试题回答

image.png

事务失效

事务管理通过声明式事务和编程式事务来提供对事务的支持。在某些场景下,Spring事务可能会失效,导致事务不按预期执行。

这些场景主要包括:

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

建议回答一下前三点。

视频讲解部分内容:

异常捕获处理

image.png

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

image.png

面试题

image.png

Bean的生命周期 🚩

Spring 容器是如何管理和创建 bean 实例

方便调试和解决问题

Bean 的流程 → 代码验证

BeanDefinition

image.png

生命周期

image.png

可以参考一下这个: https://www.bilibili.com/video/BV1yT411H7YK/?p=39&spm_id_from=pageDriver&vd_source=6a019ecccfe7d8f62b9a3fe99c723bd0

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

面试题回答

image.png

循环依赖 🚩

image.png

image.png

看一下相关的解析:

关于三级缓存解决循环引用的描述,基本思路是对的,但描述过程中的一些细节需要更准确地表达:

  1. 开始实例化对象A:当Spring容器开始创建Bean A时,如果发现A依赖于B,它会暂停A的创建过程,并开始创建B。
  2. 放入三级缓存:在这个过程中,Spring容器实际上会将创建A的部分完成的实例(也就是说,这个实例已经分配了内存,但它的依赖还没有完全解决)的工厂放入三级缓存中。这个工厂负责生成A的引用,以便在需要时可以提前暴露出来。
  3. 创建对象B:在创建B的过程中,如果B需要依赖于A,Spring容器会尝试从缓存中获取A的实例。此时,它会从三级缓存中获取A的工厂,使用这个工厂创建A的早期引用,并将这个引用提升到二级缓存中。
  4. 完成对象B的创建:有了A的引用后,Spring容器可以继续B的创建过程,并解决其对A的依赖。
  5. 完成对象A的创建:一旦B被成功创建并设置到A中,Spring容器回到A的创建过程中,完成A的创建并解决其他可能的依赖。

通过三级缓存机制,Spring能够处理循环依赖的情况,允许A和B互相引用而不导致死锁或Bean创建失败。

不过,需要注意的是,Spring的这种三级缓存机制只能解决单例作用域Bean的循环依赖问题,并且主要解决的是Setter注入或字段注入造成的循环依赖。对于构造器注入导致的循环依赖,Spring是无法解决的。

👇

构造方法出现了循环依赖的话,可以使用 @Lazy 进行懒加载

image.png

常用注解

Spring框架常见注解(Spring、Springboot、Springmvc)

Spring
  • @Componetn、@Controller、@Service、@Repository
  • @Autowired
  • @Qualifier
  • @Scope
  • @Configuration
  • @ComponentScan
  • @Bean
  • @Import
  • @Aspect、@Before、@After、@Around、@Pointcut

image.png

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

image.png

SpringBoot
  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @CompnentScan

image.png

你们项目中有用过 SPI 吗,是否自己封装过组件 🚩

有没有自定义过 BeanPost xxx ,具体怎么使用的 🚩

SpringMVC

执行流程

Springmvc的执行流程是这个框架最核心的内容

  • 1、视图阶段(老旧JSP等 )
  • 2、前后端分离阶段(接口开发,异步)

视图阶段

image.png

前后端分离

image.png

面试题:

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

image.png

版本2:

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

image.png

SpringBoot

自动配置原理 🚩

这道题目待复习一下

image.png

image.png

image.png

2,其中@EnableAutoConfiguration是实现自动化配置的核心注解。

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

image.png

Mybatis

# 和 { } 替换

动态标签

if、where、set

select、foreach,

执行流程 🚩


Mybatis 执行流程

  • 1、理解各个组件的关系
  • 2、Sql 的执行过程(参数映射、sql 解析、执行和结果处理)

讲一下 mybatis 的配置文件的 xml 文件的作用有哪些?

  • 加载核心配置
  • 加载映射文件

image.png

执行流程

image.png

面试题

image.png

延迟加载 🚩

延迟加载操作:需要配置文件中开启(关联对象集合的延迟加载)

实现原理:动态代理,CGLIB

image.png

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

image.png

主要看一下这个:

image.png

一二级缓存 🚩

image.png 二级缓存

image.png

注意事项:

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

面试题:

image.png

  • 缓存

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