1.Spring框架中的单例bean是线程安全的吗?
候选人:
不是线程安全的 。当多用户同时请求一个服务时,容器会给每个请求分配一个线程,这些线程会并发执行业务逻辑。如果处理逻辑中包含对单例状态的修改,比如修改单例的成员属性( 比如某个Controller里面用了一个int count 成员变量,就需要考虑线程安全性**)**,就必须考虑线程同步问题。Spring框架本身并不对单例bean进行线程安全封装,线程安全和并发问题需要开发者自行处理。
通常在项目中使用的Spring bean是不可变状态 (即一般不进行修改的Bean,如注入的service,只是调用业务方法,而不会去修改它的属性和方法),因此在某种程度上可以说Spring的单例bean是线程安全的。如果bean有多种状态(如ViewModel对象),就需要自行保证线程安全。最简单的解决办法是将单例bean的作用域由"singleton"变更为"prototype"。
2.什么是AOP?
候选人:
AOP,即面向切面编程 ,在Spring中用于将那些与业务无关但对多个对象产生影响的公共行为和逻辑抽取出来,实现公共模块复用,降低耦合 。常见的应用场景包括公共日志保存 和事务处理。
3.项目中有没有使用到AOP?
候选人:
我们之前在后台管理系统中使用AOP来记录系统操作日志 。主要思路是使用AOP的环绕通知 和切点表达式,找到需要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,例如类信息、方法信息、注解、请求方式等,并将这些参数保存到数据库。
4.Spring中的事务是如何是实现的?
候选人:
Spring实现事务的本质是利用AOP完成的。它对方法前后进行拦截,在执行方法前开启事务,在执行完目标方法后根据执行情况提交或回滚事务。
5.Spring中事务失效的场景有哪些?
候选人:
在项目中,我遇到过几种导致事务失效的场景:
-
如果方法内部捕获并处理了异常,没有将异常抛出,会导致事务失效。因此,处理异常后应该确保异常能够被抛出。
-
如果方法抛出检查型异常 (checked exception),并且没有在**
@Transactional注解** 上配置**rollbackFor属性** 为**Exception**,那么异常发生时事务可能不会回滚。 -
如果事务注解的方法不是公开(public)修饰的,也可能导致事务失效。
6.Spring的bean生命周期?
候选人:
Spring中bean的生命周期包括以下步骤:
-
通过**
BeanDefinition**获取bean的定义信息。 -
调用构造函数实例化bean。
-
进行bean的依赖注入 ,例如通过setter方法或
@Autowired注解。 -
处理实现了**
Aware接口的bean**。 -
执行**
BeanPostProcessor的前置处理器**。 -
调用初始化方法 ,如实现了
InitializingBean接口或自定义的init-method。 -
执行**
BeanPostProcessor的后置处理器**,可能在这里产生代理对象。 -
最后是销毁bean。
7.Spring中的循环引用?
候选人:
循环依赖发生在两个或两个以上的bean互相持有对方,形成闭环。Spring框架允许循环依赖存在,并通过三级缓存解决大部分循环依赖问题:
-
一级缓存:单例池,缓存已完成初始化的bean对象。
-
二级缓存:缓存尚未完成生命周期的早期bean对象。
-
三级缓存:缓存
ObjectFactory,用于创建bean对象。
8.循环引用的具体解决流程能说一下吗?
候选人:
解决循环依赖的流程如下:
-
实例化A对象,并创建
ObjectFactory存入三级缓存。 -
A在初始化时需要B对象,开始B的创建逻辑。
-
B实例化完成,也创建
ObjectFactory存入三级缓存。 -
B需要注入A,通过三级缓存获取
ObjectFactory生成A对象,存入二级缓存。 -
B通过二级缓存获得A对象后,B创建成功,存入一级缓存。
-
A对象初始化时,由于B已创建完成,可以直接注入B,A创建成功存入一级缓存。
-
清除二级缓存中的临时对象A。
9.构造方法出现了循环依赖怎么解决?
候选人:
由于构造函数是bean生命周期中最先执行的,Spring框架无法解决构造方法的循环依赖问题。可以使用**@Lazy懒加载注解**,延迟bean的创建直到实际需要时。
10.SpringMVC的执行流程能说明一下吗?
候选人:
SpringMVC的执行流程包括以下步骤:
-
用户发送请求到前端控制器
DispatcherServlet。 -
Dispatcher``Servlet调用HandlerMapping找到具体处理器。 -
HandlerMapping返回处理器对象及拦截器(如果有)给DispatcherServlet。 -
DispatcherServlet调用HandlerAdapter。 -
HandlerAdapter适配并调用具体处理器(Controller)。 -
Controller执行并返回
ModelAndView对象。 -
HandlerAdapter将ModelAndView返回给DispatcherServlet。 -
DispatcherServlet传给ViewResolver进行视图解析。 -
ViewResolver返回具体视图给DispatcherServlet。 -
DispatcherServlet渲染视图并响应用户。
| 步骤序号 | 核心动作 | 核心组件 / 对象 | 大白话解释(核心作用) |
|---|---|---|---|
| 1 | 用户发送请求到 DispatcherServlet | 前端控制器 DispatcherServlet | 用户的所有 HTTP 请求(如 GET/POST)都先交给这个 "中央调度器",它是 SpringMVC 的唯一入口,相当于项目的 "前台接待",不处理业务,只负责协调其他组件 |
| 2 | DispatcherServlet 调用 HandlerMapping | 处理器映射器 HandlerMapping | 接待员(DispatcherServlet)问 "映射器":这个请求(比如/user/list)该找哪个 Controller 的哪个方法处理?(底层是匹配 @RequestMapping 注解的 URL 映射) |
| 3 | HandlerMapping 返回处理器 + 拦截器 | HandlerMapping + 拦截器 | 映射器返回目标 Controller 对象(及具体方法),同时返回配置的拦截器(比如登录校验、日志拦截器) |
| 4 | DispatcherServlet 调用 HandlerAdapter | 处理器适配器 HandlerAdapter | 接待员拿到 Controller 后,交给 "适配器"------ 因为 Controller 写法不同(注解式、实现接口式),适配器统一调用方式,避免接待员直接对接不同类型的 Controller |
| 5 | HandlerAdapter 调用具体 Controller | 处理器 Controller | 适配器按统一规则调用 Controller 的业务方法,执行核心逻辑(比如查数据库、处理请求参数、调用服务层) |
| 6 | Controller 返回 ModelAndView | Controller + ModelAndView | Controller 处理完业务,返回 "数据(Model)+ 视图名(View)":Model 是业务数据(如用户列表),View 是逻辑视图名(如user/list) |
| 7 | HandlerAdapter 返回 ModelAndView 给 DispatcherServlet | HandlerAdapter + ModelAndView | 适配器把处理结果回传给接待员 |
| 8 | DispatcherServlet 传给 ViewResolver | 视图解析器 ViewResolver | 接待员问 "解析器":逻辑视图名user/list对应哪个物理视图文件? |
| 9 | ViewResolver 返回具体视图 | ViewResolver | 解析器根据配置(前缀 + 后缀)返回物理路径(比如/WEB-INF/views/user/list.jsp) |
| 10 | DispatcherServlet 渲染视图并响应 | DispatcherServlet + 视图 | 接待员把 Model 中的数据填充到视图中(比如 JSP 渲染用户列表),生成 HTML 响应给用户 |
11.Springboot自动配置原理能说明一下吗?
候选人:
Spring Boot的自动配置原理基于**@SpringBootApplication注解** ,它封装了**@SpringBootConfiguration** 、@EnableAutoConfiguration 和**@ComponentScan** 。@EnableAutoConfiguration是核心,它通过@Import导入配置选择器,读取META-INF/spring.factories文件中的类名,根据条件注解决定是否将配置类中的Bean导入到Spring容器中。
12.Spring的常见注解?
候选人:
Spring的常见注解包括:
-
声明Bean的注解:
@Component、@Service、@Repository、@Controller。 -
依赖注入相关注解:
@Autowired、@``Qualifier、@Resource。 -
设置作用域的注解:
@Scope。 -
配置相关注解:
@Configuration、@ComponentScan、@Bean。 -
AOP相关注解:
@Aspect、@Before、@After、@Around、@Pointcut。
13.SpringMVC常见的注解有哪些?
候选人:
SpringMVC的常见注解有:
-
@RequestMapping:映射请求路径。 -
@RequestBody:接收HTTP请求的JSON数据。 -
@RequestParam:指定请求参数名称。 -
@PathVariable:从请求路径中获取参数。 -
@ResponseBody:将Controller方法返回的对象转化为JSON。 -
@RequestHeader:获取请求头数据。 -
@PostMapping、@GetMapping等。
注:SpringMVC大白话理解为帮我搞定 "用户浏览器发请求→后端处理请求→返回响应" 的 "一站式工具包" ------ 不用自己写复杂的 Servlet、处理请求参数、适配不同的返回格式(JSP/JSON),SpringMVC 已经封装好了所有核心逻辑,只需要关注 "业务逻辑(比如查用户、改订单)" 即可。
14.Springboot常见的注解有哪些?
候选人:
Spring Boot的常见注解包括:
-
@SpringBootApplication:由@``SpringBootConfiguration、@``EnableAutoConfiguration和@``ComponentScan组成。 -
其他注解如
@``RestController、@GetMapping、@PostMapping等,用于简化Spring MVC的配置。
15.MyBatis的执行流程说一下?
候选人:
MyBatis的执行流程如下:
-
读取MyBatis配置文件
mybatis-config.xml。 -
构造会话工厂
SqlSessionFactory。 -
会话工厂创建
SqlSession对象。 -
操作数据库的接口,
Executor执行器。 -
Executor执行方法中的MappedStatement参数。 -
输入参数映射。
-
输出结果映射。
16.MyBatis是否支持延迟加载?
候选人:
MyBatis支持延迟加载,即在需要用到数据时才加载。可以通过配置文件中的lazyLoadingEnabled配置启用或禁用延迟加载。
注:MyBatis 的延迟加载是指在查询带有关联关系的数据时(比如用户和订单、订单和商品),不会在查询主数据的同时立即加载关联数据,而是等到你「真正需要使用」这些关联数据时,才会触发数据库查询。
17.延迟加载的底层原理知道吗?
候选人:
延迟加载的底层原理主要使用CGLIB动态代理实现:
-
使用CGLIB创建目标对象的代理对象。
-
调用目标方法时,如果发现是null值,则执行SQL查询。
-
获取数据后,设置属性值并继续查询目标方法。
18.Mybatis的一级、二级缓存用过吗?
候选人:
MyBatis的一级缓存是基于Perpetual``Cache的HashMap本地缓存,作用域为Session,默认开启。二级缓存需要单独开启,作用域为Namespace或mapper,默认也是采用PerpetualCache,HashMap存储。
注:
MyBatis 的缓存是为了减少数据库查询次数,将查询结果临时存储在内存中,后续相同查询请求直接从缓存取数据,无需访问数据库,核心分为两级:
- 一级缓存:SqlSession(会话)级别的本地缓存,默认开启,仅当前 SqlSession 可用;
可以把 SqlSession 理解为 "一次数据库会话",一级缓存就是这个会话的 "临时记事本":你查过一次 "id=1 的用户",记事本就记下来,下次再问,直接念记事本内容,不用再去问数据库。
- 二级缓存:Mapper(命名空间)级别的全局缓存,需手动开启,跨 SqlSession 共享。
二级缓存是Mapper(命名空间 namespace)级别的全局缓存,相当于多个 SqlSession 共享的 "公共记事本"
19.Mybatis的二级缓存什么时候会清理缓存中的数据?
候选人:
当作用域(一级缓存Session/二级缓存Namespaces)进行了新增、修改、删除操作后,默认该作用域下所有select中的缓存将被清空。