📅 当前时间: 🏠 返回顶部 📧 邮箱:contact@kaoacca.cn 📱 关注公众号:考ACCA网

SSM框架高级面试题|Spring + SpringMVC + MyBatis 核心技术深度解析

覆盖Spring IoC/AOP、SpringMVC请求流程、MyBatis动态SQL、事务管理、整合陷阱等高级开发岗必考知识点,结合真实项目场景,助您系统梳理知识体系,从容应对技术面试。

⚡ 一、SSM框架全景概览:为何仍是Java开发的“基石组合”?

尽管Spring Boot已成为主流,但SSM(Spring + SpringMVC + MyBatis)组合仍是Java企业级开发的黄金标准,尤其在传统行业、金融系统、政府项目中广泛使用。其优势在于:
① 模块解耦:三层架构清晰分离业务逻辑、控制层与数据访问层;
② 高度可配置:XML或注解双模式支持,灵活适配不同规模项目;
③ 生态成熟稳定:十年以上生产验证,文档完善、社区活跃、问题易定位。

⚙️ Spring:企业级应用核心容器

提供IoC容器管理对象生命周期,AOP支持日志、事务、权限等横切关注点。高级面试常考:Bean作用域、循环依赖解决方案、事务传播机制。

⚡ SpringMVC:请求驱动的Web框架

基于前端控制器模式(DispatcherServlet),支持RESTful、参数绑定、拦截器链。高频考点:请求处理流程、HandlerMapping机制、视图解析策略。

💾 MyBatis:半自动SQL映射框架

SQL与代码分离,支持动态SQL、缓存、插件机制。面试重点:一级/二级缓存原理、Mapper动态代理、插件拦截链。

〔深度解析〕SSM vs Spring Boot:技术演进中的定位差异

许多求职者混淆SSM与Spring Boot的关系。需明确:Spring Boot是Spring生态的“加速器”,它简化配置、自动装配,但底层仍依赖Spring核心。SSM是手动整合三者的传统方案,而Spring Boot项目可通过mybatis-spring-boot-starter无缝集成MyBatis。

例如:一个SSM项目中,Spring配置需手动编写applicationContext.xml与spring-mvc.xml;而在Spring Boot中,仅需添加依赖并配置application.yml即可自动注入MyBatis的SqlSessionFactory。

// SSM项目典型配置结构 src/ ├── main/ │ ├── java/ │ │ └── com/example/service/ │ │ └── UserService.java │ ├── resources/ │ │ ├── applicationContext.xml // Spring核心配置 │ │ ├── spring-mvc.xml // SpringMVC配置 │ │ └── mybatis-config.xml // MyBatis配置 │ └── webapp/ │ └── WEB-INF/ │ └── web.xml // Web入口配置

因此,SSM框架高级面试题考察的不仅是框架使用,更是对Java Web技术栈演进逻辑的理解深度——面试官常通过“SSM如何过渡到Spring Boot”验证候选人技术前瞻性。

⚙️ 二、Spring核心:从IoC到AOP的高级考点解析

Spring作为企业级开发的基石,其IoC容器与AOP机制是面试必考项。以下聚焦高级场景,覆盖高频陷阱与最佳实践。

〔1〕IoC容器:Bean生命周期与循环依赖

问题示例:Spring中一个Bean从创建到销毁经历了哪些阶段?循环依赖如何解决?

标准答案框架:
① 实例化(Constructor)→ ② 属性赋值(Populate)→ ③ 初始化(Aware接口→InitMethod→PostProcessor)→ ④ 销毁(Destroy)
其中关键节点:
- BeanNameAware:获取Bean名称
- BeanFactoryAware:获取工厂引用
- InitializingBean与@PostConstruct:初始化逻辑
- DisposableBean与@PreDestroy:销毁前清理

※ 循环依赖解决方案(三级缓存)

Spring通过三级缓存解决单例Bean的循环依赖:
① singletonObjects(一级缓存):存放完整初始化后的单例Bean
② earlySingletonObjects(二级缓存):存放早期暴露的Bean(未完成属性填充)
③ singletonFactories(三级缓存):存放ObjectFactory用于生成早期Bean

注意:循环依赖仅支持@Autowired字段注入,不支持构造器注入(会抛出BeanCurrentlyInCreationException)。

〔2〕AOP:动态代理与事务管理

问题示例:Spring AOP如何实现?JDK动态代理与CGLIB区别?事务传播行为有哪些?

JDK动态代理:基于接口实现,要求目标类实现接口;生成代理对象调用InvocationHandler的invoke()方法。
CGLIB代理:通过继承生成子类,适用于无接口场景;需引入cglib依赖;Spring 5后默认使用spring-core中的Objenesis绕过构造器限制。

面试陷阱:当类同时存在接口与无接口情况,Spring如何选择?
→ 默认策略:
- 类实现接口 → 使用JDK代理
- 类无接口 → 使用CGLIB代理
→ 可通过@EnableAspectJAutoProxy(proxyTargetClass=true)强制CGLIB。

7种事务传播行为:
① REQUIRED:有事务则加入,否则新建(最常用)
② REQUIRES_NEW:挂起当前事务,新建事务
③ SUPPORTS:支持当前事务,无则非事务执行
④ NOT_SUPPORTED:挂起当前事务,以非事务执行
⑤ NEVER:以非事务执行,有事务则抛异常
⑥ NESTED:在当前事务内创建保存点(Savepoint)
⑦ MANDATORY:必须在事务内执行,否则异常

经典案例:订单服务调用库存服务,库存服务设置REQUIRES_NEW。若库存扣减失败回滚,订单服务是否回滚?
→ 否!因库存操作在独立事务中,回滚不影响订单主事务。

自定义注解切面示例:

// 自定义注解 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Loggable { String value() default ""; } // 切面实现 @Aspect @Component public class LogAspect { @Around("@annotation(loggable)") public Object log(ProceedingJoinPoint pjp, Loggable loggable) throws Throwable { long start = System.currentTimeMillis(); Object result = pjp.proceed(); long cost = System.currentTimeMillis() - start; System.out.println("执行" + loggable.value() + "耗时:" + cost + "ms"); return result; } }

使用:@Loggable("用户登录") public void login() {...}

⚡ 三、SpringMVC机制:请求处理全流程与高级特性

SpringMVC以DispatcherServlet为核心,采用前端控制器模式。面试常考请求流转细节与异常处理策略。

〔1〕请求处理完整流程

标准流程(7步):
① 客户端请求 → DispatcherServlet
② HandlerMapping查找Handler(如Controller方法)
③ HandlerAdapter适配执行Handler
④ Handler返回ModelAndView
⑤ ViewResolver解析视图
⑥ 渲染视图(数据填充)
⑦ 响应客户端

〔〕 HandlerMapping优先级与自定义

SpringMVC支持多种HandlerMapping:
- RequestMappingHandlerMapping:处理@RequestMapping注解(默认最高优先级)
- BeanNameUrlHandlerMapping:按Bean名称映射URL
- SimpleUrlHandlerMapping:自定义URL到Handler映射

通过order属性调整优先级(数值越小优先级越高),但需注意:多个Mapping匹配同一URL时,仅第一个生效。

〔2〕参数绑定与数据校验

高级考点:
- @RequestBody:JSON转Java对象(依赖Jackson)
- @Valid + @Validated:JSR-303校验(分组校验支持)
- @InitBinder:自定义属性编辑器(如日期格式化)

// 分组校验示例 public interface CreateGroup {} public interface UpdateGroup {} public class User { @NotNull(groups = UpdateGroup.class) private Long id; @NotBlank(groups = CreateGroup.class) private String username; } @PostMapping("/user") public String save(@Validated(CreateGroup.class) @RequestBody User user) { // 创建时校验username,更新时校验id }

〔3〕拦截器与全局异常处理

↑ 拦截器(HandlerInterceptor)

三个核心方法:
- preHandle():请求前执行,返回true继续
- postHandle():视图渲染前执行
- afterCompletion():请求完成后执行(异常也触发)

应用场景:登录校验、性能监控、日志记录。

♀ 全局异常处理(@ControllerAdvice)

统一处理异常,避免在Controller中写try-catch:
- @ExceptionHandler:指定异常类型
- @ResponseStatus:设置HTTP状态码
- @ResponseBody:返回JSON格式异常信息

典型错误:拦截器中通过request.getRequestDispatcher().forward()跳转时,postHandle()仍会执行。正确做法:直接返回false并设置响应。

💾 四、MyBatis深度:动态SQL、缓存与插件机制

MyBatis作为半自动SQL映射框架,其灵活性与性能优势使其在复杂查询场景中不可替代。面试高频考察动态SQL、缓存原理及插件扩展。

〔1〕动态SQL与性能陷阱

核心标签:<if>、<choose>、<where>、<set>、<foreach>

性能陷阱:
- <foreach>中使用collection="list"时,若传入的是数组,应改为collection="array"
- 避免在<if>中写复杂逻辑(如age > 18 and age < 30),应拆分为多个<if>提升可读性
- 使用useCache="false"关闭查询缓存(防止脏读)

〔2〕缓存机制深度解析

原理:基于SqlSession的Map缓存,同一SqlSession内相同查询直接返回缓存。
失效场景:
① 执行增删改操作(默认清空缓存)
② 手动调用sqlSession.clearCache()
③ 跨SqlSession查询(不同会话互不影响)

面试陷阱:MyBatis默认开启一级缓存,但Spring整合后因每次请求创建新SqlSession,一级缓存实际失效!正确做法是启用二级缓存或使用Spring Cache。

配置步骤:
① Mapper XML中添加<cache/>
② 实体类实现Serializable接口
③ 全局配置settings中开启cacheEnabled=true

缓存策略:
- EternalCache:无限期缓存
- LruCache:最近最少使用淘汰
- FifoCache:先进先出淘汰
- SoftCache:软引用(JVM GC时回收)

问题:二级缓存如何解决脏数据?
→ 通过@CacheNamespaceRef指定命名空间共享缓存;增删改操作自动刷新缓存。

〔3〕插件机制(Interceptor)

MyBatis四大可插件对象:Executor、StatementHandler、ParameterHandler、ResultSetHandler。

※ 分页插件实战(PageHelper)

PageHelper.startPage(pageNum, pageSize)通过拦截StatementHandler的prepare()方法,在SQL后追加LIMIT子句实现物理分页。
关键点:
- 必须在Mapper调用前执行startPage
- 仅对紧随其后的第一个查询生效
- 支持Oracle等不支持LIMIT的数据库(通过ROWNUM实现)

〔〕 五、SSM整合实战:配置陷阱与性能优化

SSM整合涉及Spring与SpringMVC容器分离、事务管理配置等复杂场景,是高级面试的重灾区。

〔1〕容器分离设计

典型架构:
- rootApplicationContext(Spring):管理Service、DAO层Bean
- WebApplicationContext(SpringMVC):管理Controller层Bean

原因:避免Controller层Bean被重复创建,确保事务切面仅作用于Service层。

// web.xml配置 org.springframework.web.context.ContextLoaderListener contextConfigLocation classpath:applicationContext.xml dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:spring-mvc.xml 1

〔2〕事务管理配置

错误配置:在SpringMVC配置文件中声明事务,导致Service层事务失效!
正确做法:
① Spring配置文件(applicationContext.xml)中:
- 扫描除Controller外的所有组件:<context:component-scan base-package="com.example" exclude-filter="...Controller"/>
- 配置事务管理器与注解驱动:<tx:annotation-driven transaction-manager="transactionManager"/>
② SpringMVC配置文件中:
- 仅扫描Controller:<context:component-scan base-package="com.example.controller"/>

〔3〕性能优化实战

⚡ 数据库连接池

推荐使用HikariCP(Spring Boot默认):
- 最小连接数=5,最大连接数=20
- 连接超时=30000ms
- 空闲超时=600000ms

MyBatis配置:<property name="poolMaximumActiveConnections" value="20"/>

💾 SQL优化

- 避免SELECT *,指定必要字段
- 大表分页用WHERE id > #{lastId} LIMIT #{size}替代LIMIT offset, size
- 动态SQL中使用<bind>预处理复杂条件

经典问题:SSM项目中如何避免N+1查询?
→ 使用MyBatis的<association>或<collection>配置fetchType="lazy"实现延迟加载;或通过JOIN一次性查询。

🔥 六、网友最关心的SSM高级面试题(TOP 10)

2024-11-15

⑴ Spring Boot和SSM如何选型?

答:新项目优先考虑Spring Boot(自动配置、内嵌Tomcat、简化依赖);老项目维护或需高度定制化(如多数据源、复杂事务)时,SSM更灵活。金融系统常选SSM因审计要求需精确控制每一层。

2024-11-20

⑵ Spring事务失效的5种场景

答:① 非public方法;② 同类方法调用;③ 异常被catch未抛出;④ 数据库引擎不支持事务(如MySQL MyISAM);⑤ 事务传播行为设置错误(如NEVER)。
解决方案:②中通过AopContext.currentProxy()获取代理调用;③中抛出RuntimeException或设置rollbackFor。

2024-11-25

⑶ MyBatis中#{}和${}的区别

答:#{}=PreparedStatement预编译参数(防SQL注入),${}=字符串替换(动态表名/列名)。
安全示例:动态排序字段用${orderBy},但需手动校验白名单;数据值必须用#{}。

2024-11-28

⑷ SpringMVC请求乱码解决方案

答:① 请求:配置CharacterEncodingFilter;② 响应:Controller添加@ResponseBody + produces="application/json;charset=UTF-8";③ 全局:Tomcat的server.xml设置URIEncoding="UTF-8"。

2024-12-01

⑸ 如何设计高可用的SSM项目?

答:① 分布式:Nginx负载均衡+集群部署;② 缓存:Redis缓存热点数据;③ 数据库:读写分离+分库分表;④ 限流:Sentinel;⑤ 降级:Hystrix。SSM项目需手动整合这些组件,Spring Boot则有starter简化集成。

〔〕 七、SSM高级面试题高频考点速查表

考察维度 核心问题 关键答案要点
Spring IoC Bean作用域有哪些? singleton(默认)、prototype、request、session、globalSession。多线程下singleton需注意线程安全(无状态Bean)。
Spring AOP 如何实现方法权限控制? 自定义注解+环绕通知。在ProceedingJoinPoint.proceed()前检查用户权限,失败则抛出异常。
SpringMVC RESTful中PUT/DELETE请求405错误? 配置HiddenHttpMethodFilter,前端表单用<input type="hidden" name="_method" value="put"/>。
MyBatis 如何实现批量插入? XML中<insert>使用<foreach collection="list" item="item" separator=",">拼接VALUES,或使用JDBC批量提交(ExecutorType.BATCH)。
整合问题 Spring与SpringMVC容器重复扫描问题? 用<context:component-scan>的exclude-filter与include-filter精确控制扫描范围。

〔补充〕SSM框架高级面试题参考答案(精简版)

  • Spring Boot自动配置原理:@SpringBootApplication → @EnableAutoConfiguration → AutoConfigurationImportSelector → 读取META-INF/spring.factories中的EnableAutoConfiguration类,通过@Conditional注解判断是否生效。
  • MyBatis一级缓存失效:Spring整合后每次请求创建新SqlSession,一级缓存自然失效;需启用二级缓存或使用Spring Cache。
  • 事务传播行为选择:核心业务用REQUIRED;独立子任务(如发送通知)用REQUIRES_NEW;非核心操作用SUPPORTS。

〔〕 九、网友们还关心的SSM框架问题

〔〕 SSM项目如何做性能压测?

答:① 工具:JMeter/LoadRunner;② 场景:模拟登录→搜索→下单→支付;③ 指标:TPS、响应时间、错误率;④ 优化:数据库慢查询分析(EXPLAIN)、线程池调优。

〔〕 如何向面试官提问SSM相关问题?

答:① 问技术演进:“SSM项目在微服务改造中遇到的最大挑战?”
② 问实践细节:“贵司如何解决SSM的分布式事务问题?”
③ 问未来规划:“是否考虑迁移到Spring Boot 3.x?”

提示:提问体现技术深度与团队意识,避免问“SSM好还是Spring Boot好”。

〔附〕SSM框架高级面试题学习资源

  • 官方文档:Spring Framework、MyBatis 3
  • 开源项目:Spring Boot(参考SSM整合逻辑)
  • 书籍:《Spring Boot实战》(第5章SSM迁移)、《深入理解Java虚拟机》(Spring容器原理)
斌知网
蜀ICP备2026017620号-5