SSM框架高频考点
目录
Spring框架中的单例bean是线程安全的吗?
什么是AOP?
你们项目中有没有使用到AOP?
Spring中的事务是如何实现的?
Spring中事务失效的场景有哪些?
非事务方法调用事务方法
事务传播行为不对
没有被Spring管理
Spring的bean的生命周期?
Spring中的循环引用?
那具体解决流程清楚吗?
构造方法出现了循环依赖怎么解决?
SpringMVC的执行流程?
Springboot自动配置原理?
Spring 的常见注解有哪些?
SpringMVC常见的注解有哪些?
Springboot常见注解有哪些?
MyBatis执行流程?
Mybatis是否支持延迟加载?
延迟加载的底层原理知道吗?
Mybatis的一级、二级缓存用过吗?
Mybatis的二级缓存什么时候会清理缓存中的数据?
Spring框架中的单例bean是线程安全的吗?
候选人:
不是线程安全的。当多用户同时请求一个服务时,容器会给每个请求分配一个线程,这些线程会并发执行业务逻辑。如果处理逻辑中包含对单例状态的修改,比如修改单例的成员属性,就必须考虑线程同步问题。Spring框架本身并不对单例bean进行线程安全封装,线程安全和并发问题需要开发者自行处理。
通常在项目中使用的Spring bean是不可变状态(如Service类和DAO类),因此在某种程度上可以说Spring的单例bean是线程安全的。如果bean有多种状态(如ViewModel对象),就需要自行保证线程安全。最简单的解决办法是将单例bean的作用域由“singleton”变更为“prototype”。
不可变状态:即一般不进行修改的Bean,如注入的service,只是调用业务方法,而不会去修改它的属性和方法
什么是AOP?
候选人:
AOP,即面向切面编程,在Spring中用于将那些与业务无关但对多个对象产生影响的公共行为和逻辑抽取出来,实现公共模块复用,降低耦合。常见的应用场景包括公共日志保存和事务处理。
你们项目中有没有使用到AOP?
候选人:
我们之前在后台管理系统中使用AOP来记录系统操作日志。主要思路是使用AOP的环绕通知和切点表达式,找到需要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,例如类信息、方法信息、注解、请求方式等,并将这些参数保存到数据库。
Spring中的事务是如何实现的?
候选人:
Spring实现事务的本质是利用AOP完成的。它对方法前后进行拦截,在执行方法前开启事务,在执行完目标方法后根据执行情况提交或回滚事务。

Spring中事务失效的场景有哪些?
候选人:
在项目中,我遇到过几种导致事务失效的场景:
- 如果方法内部捕获并处理了异常,没有将异常抛出,会导致事务失效。因此,处理异常后应该确保异常能够被抛出。
- 如果方法抛出检查型异常(checked exception),并且没有在
@Transactional注解上配置rollbackFor属性为Exception,那么异常发生时事务可能不会回滚。默认只会检查runtimeException。 - 如果事务注解的方法不是公开(public)修饰的,也可能导致事务失效。

非事务方法调用事务方法
有这样一段代码:
@Service
public class OrderService {public void createOrder(){// ... 准备订单数据// 生成订单并扣减库存insertOrderAndReduceStock();}@Transactionalpublic void insertOrderAndReduceStock(){// 生成订单insertOrder();// 扣减库存reduceStock();}
}
可以看到,insertOrderAndReduceStock方法是一个事务方法,肯定会被Spring事务管理。Spring会给OrderService类生成一个动态代理对象,对insertOrderAndReduceStock方法做增加,实现事务效果。
但是现在createOrder方法是一个非事务方法,在其中调用了insertOrderAndReduceStock方法,这个调用其实隐含了一个this.的前缀。也就是说,这里相当于是直接调用原始的OrderService中的普通方法,而非被Spring代理对象的代理方法。那事务肯定就失效了!
事务传播行为不对
示例代码:
@Servicepublic class OrderService {@Transactionalpublic void createOrder(){// 生成订单insertOrder();// 扣减库存reduceStock();throw new RuntimeException("业务异常");}@Transactionalpublic void insertOrder() {}@Transactional(propagation = Propagation.REQUIRES_NEW)public void reduceStock() {}}
在示例代码中,事务的入口是createOrder()方法,会开启一个事务,可以成为外部事务。在createOrder()方法内部又调用了insertOrder()方法和reduceStock()方法。这两个都是事务方法。
不过,reduceStock()方法的事务传播行为是REQUIRES_NEW,这会导致在进入reduceStock()方法时会创建一个新的事务,可以成为子事务。insertOrder()则是默认,因此会与createOrder()合并事务。
因此,当createOrder方法最后抛出异常时,只会导致insertOrder方法回滚,而不会导致reduceStock方法回滚,因为reduceStock是一个独立事务。
所以,一定要慎用传播行为,注意外部事务与内部事务之间的关系。
没有被Spring管理
示例代码:
// @Servicepublic class OrderService {@Transactionalpublic void createOrder(){// 生成订单insertOrder();// 扣减库存reduceStock();throw new RuntimeException("业务异常");}@Transactionalpublic void insertOrder() {}@Transactionalpublic void reduceStock() {}}
这个示例属于比较低级的错误,OrderService类没有添加@Service注解,因此就没有被Spring管理。你在方法上添加的@Transactional注解根本不会有人帮你动态代理,事务自然失效。
Spring的bean的生命周期?
候选人:
Spring中bean的生命周期包括以下步骤:
- 通过
BeanDefinition获取bean的定义信息。 - 调用构造函数实例化bean。
- 进行bean的依赖注入,例如通过setter方法或
@Autowired注解。 - 处理实现了
Aware接口的bean。 - 执行
BeanPostProcessor的前置处理器。 - 调用初始化方法,如实现了
InitializingBean接口或自定义的init-method。 - 执行
BeanPostProcessor的后置处理器,可能在这里产生代理对象。 - 最后是销毁bean。
Spring中的循环引用?
候选人:
循环依赖发生在两个或两个以上的bean互相持有对方,形成闭环。Spring框架允许循环依赖存在,并通过三级缓存解决大部分循环依赖问题:
- 一级缓存:单例池,缓存已完成初始化的bean对象。
- 二级缓存:缓存尚未完成生命周期的早期bean对象。
- 三级缓存:缓存
ObjectFactory,用于创建bean对象。
那具体解决流程清楚吗?
候选人:
解决循环依赖的流程如下:
- 实例化A对象,并创建
ObjectFactory存入三级缓存。 - A在初始化时需要B对象,开始B的创建逻辑。
- B实例化完成,也创建
ObjectFactory存入三级缓存。 - B需要注入A,通过三级缓存获取
ObjectFactory生成A对象,存入二级缓存。 - B通过二级缓存获得A对象后,B创建成功,存入一级缓存。
- A对象初始化时,由于B已创建完成,可以直接注入B,A创建成功存入一级缓存。
- 清除二级缓存中的临时对象A。

构造方法出现了循环依赖怎么解决?
候选人:
由于构造函数是bean生命周期中最先执行的,Spring框架无法解决构造方法的循环依赖问题。可以使用@Lazy懒加载注解,延迟bean的创建直到实际需要时。
SpringMVC的执行流程?
候选人:
SpringMVC的执行流程包括以下步骤:
- 用户发送请求到前端控制器
DispatcherServlet。 DispatcherServlet调用HandlerMapping找到具体处理器。HandlerMapping返回处理器对象及拦截器(如果有)给DispatcherServlet。DispatcherServlet调用HandlerAdapter。HandlerAdapter适配并调用具体处理器(Controller)。- Controller执行并返回
ModelAndView对象。 HandlerAdapter将ModelAndView返回给DispatcherServlet。DispatcherServlet传给ViewResolver进行视图解析。ViewResolver返回具体视图给DispatcherServlet。DispatcherServlet渲染视图并响应用户。



两个情况:一个是返回ModelAndView,一个是对象,就是上面的视图阶段和前后端分离阶段
自己理解:
1. 客户端发送请求到DispatcherServlet (前端控制器)
2. DispatcherServlet 通过 HandlerMapping(处理映射器) 查找到一个处理器(handler)
3. DispatcherServlet 通过 HandlerAdapter(处理适配器)调用并适配具体的处理器执行 Handler,执行结果返回一个 ModelAndView 对象
4.DispatcherServlet 会调用 ViewResolver(视图解析器) 解析ModelAndView ,返回一个view对象
5.最后DispatcherServlet 进行渲染视图
前后端分离阶段:
1. 客户端发送请求到DispatcherServlet (前端控制器)
2. DispatcherServlet 通过 HandlerMapping(处理映射器) 查找到一个处理器(handler)
3. DispatcherServlet 通过 HandlerAdapter(处理适配器)调用并适配具体的处理器执行 Handler,返回一个对象( @ResponseBody标记的对象)
4.DispatcherServlet 会跳过视图解析,直接通过 HttpMessageConverter 将对象序列化 JSON并响应给客户端。
Springboot自动配置原理?
候选人:
Spring Boot的自动配置原理基于@SpringBootApplication注解,它封装了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。@EnableAutoConfiguration是核心,它通过@Import导入配置选择器,读取META-INF/spring.factories文件中的类名,根据条件注解决定是否将配置类中的Bean导入到Spring容器中。
自己理解:
Spring Boot的自动配置原理基于@SpringBootApplication注解,它封装@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan。@EnableAutoConfiguration是核心,@EnableAutoConfiguration注解内部使用 @Import导入了 AutoConfigurationImportSelector类,
通过该类读取 META-INF/spring.factories文件,加载其中定义的自动配置类(Auto-Configuration Classes)。自动配置类本身是带有条件判断的 @Configuration类,通过条件注解来控制是否生效。



Spring 的常见注解有哪些?
候选人:
Spring的常见注解包括:
- 声明Bean的注解:
@Component、@Service、@Repository、@Controller。 - 依赖注入相关注解:
@Autowired、@Qualifier、@Resource。 - 设置作用域的注解:
@Scope。 - 配置相关注解:
@Configuration、@ComponentScan、@Bean。 - AOP相关注解:
@Aspect、@Before、@After、@Around、@Pointcut。

SpringMVC常见的注解有哪些?
候选人:
SpringMVC的常见注解有:
@RequestMapping:映射请求路径。@RequestBody:接收HTTP请求的JSON数据。@RequestParam:指定请求参数名称。@PathVariable:从请求路径中获取参数。@ResponseBody:将Controller方法返回的对象转化为JSON。@RequestHeader:获取请求头数据。@PostMapping、@GetMapping等。

Springboot常见注解有哪些?
候选人:
Spring Boot的常见注解包括:
@SpringBootApplication:由@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan组成。- 其他注解如
@RestController、@GetMapping、@PostMapping等,用于简化Spring MVC的配置。

MyBatis执行流程?
候选人:
MyBatis的执行流程如下:
- 读取MyBatis配置文件
mybatis-config.xml。 - 构造会话工厂
SqlSessionFactory。 - 会话工厂创建
SqlSession对象。 - 操作数据库的接口,
Executor执行器。 Executor执行方法中的MappedStatement参数。(映射我们mapper 中的标签页)- 输入参数映射。
- 输出结果映射。
Mybatis是否支持延迟加载?
候选人:
MyBatis支持延迟加载,即在需要用到数据时才加载。可以通过配置文件中的lazyLoadingEnabled配置启用或禁用延迟加载。
延迟加载的底层原理知道吗?
候选人:
延迟加载的底层原理主要使用CGLIB动态代理实现:
- 使用CGLIB创建目标对象的代理对象。
- 调用目标方法时,如果发现是null值,则执行SQL查询。
- 获取数据后,设置属性值并继续查询目标方法。
延迟加载的底层原理:
延迟加载的核心思想是延迟到实际需要时才加载数据,以避免在开始时就加载所有数据,从而提高性能。
代理模式:在使用延迟加载时,通常会通过代理模式来实现,最常用的是通过 CGLIB 或 JDK动态代理 创建一个代理对象。
代理对象:目标对象(例如数据库查询对象)会被代理,代理对象并不会立即加载数据,而是通过 懒加载 机制,在实际调用时才去执行数据的加载。
触发加载:当你访问某个属性时,如果该属性的数据尚未加载,代理对象会检测到并触发真正的数据加载操作。例如,在访问一个集合属性时,代理会判断是否已经从数据库加载数据,如果没有加载,它会执行 SQL 查询获取数据。
数据加载:在首次访问时,代理对象会调用底层数据库查询,执行 SQL 查询来加载实际数据。查询完成后,数据被缓存到目标对象的属性中,以便后续访问时无需再次查询数据库。
Mybatis的一级、二级缓存用过吗?
候选人:
MyBatis的一级缓存是基于PerpetualCache的HashMap本地缓存,作用域为Session,默认开启。二级缓存需要单独开启,作用域为Namespace或mapper,默认也是采用PerpetualCache,HashMap存储。

Mybatis的二级缓存什么时候会清理缓存中的数据?
候选人:
当作用域(一级缓存Session/二级缓存Namespaces)进行了新增、修改、删除操作后,默认该作用域下所有select中的缓存将被清空。

