Java框架常见面试题详解|Spring/Spring Boot/MyBatis/微服务高频考点全覆盖
一、为什么Java框架面试题是技术面试的“必考项”?
在Java后端开发岗位招聘中,框架知识占比高达60%以上。企业不仅考察候选人对API的调用熟练度,更关注其对框架设计思想、底层原理、组件协同机制的深度理解。例如:
“能说出@Autowired的默认注入方式是byType,不如能讲清Spring容器启动时BeanDefinition的注册与合并流程”——某大厂技术总监
本页面系统梳理Java框架常见面试题,覆盖:
- Spring家族:Spring Core、AOP、事务、MVC、Boot、Cloud
- 持久层:MyBatis核心机制、动态SQL、插件体系
- 中间件与微服务:Dubbo、Nacos、Sentinel、Seata
- 工程实践:性能调优、故障排查、高并发设计
所有内容均基于2024年最新技术生态整理,含真实面试案例、代码片段、时序图说明,助您构建完整知识体系。
Spring核心框架高频面试题
【Spring容器启动流程】请简述Spring容器初始化的完整过程
Spring容器启动本质是ApplicationContext的创建与刷新过程,核心步骤如下:
XmlBeanDefinitionReader加载XML或AnnotatedBeanDefinitionReader扫描注解,将配置元数据解析为BeanDefinition,注册到DefaultListableBeanFactory。BeanFactoryPostProcessor(如PropertySourcesPlaceholderConfigurer),允许修改BeanDefinition属性(如占位符替换)。BeanPostProcessor实例按优先级注册到容器(注意:此时不执行,仅注册)。finishBeanFactoryInitialization(),对非懒加载单例Bean执行:① 实例化(调用构造器)→ ② 属性填充(populate)→ ③ 初始化(aware回调、
init-method)→ ④ 放入单例池高频追问:
1. @Lazy注解对单例Bean的作用?→ 推迟初始化,首次getBean时创建
2. @DependsOn如何影响初始化顺序?→ 显式指定依赖关系,确保被依赖Bean先初始化
示例:手动触发懒加载Bean
【@Autowired vs @Resource】两者的注入机制与差异
| 特性 | @Autowired(Spring) | @Resource(JSR-250) |
|---|---|---|
| 来源 | Spring框架 | Java标准规范(JSR-250) |
| 默认注入方式 | byType(类型匹配) | byName(字段名/属性名) |
| 按名称注入 | 需配合@Qualifier或字段名 |
直接通过name属性指定 |
| required属性 | 默认true,未匹配则抛异常 |
默认true,未匹配则为null |
| 支持位置 | 构造器、字段、setter方法 | 字段、setter方法 |
典型问题:当存在多个同类型Bean时如何处理?
- 方案1:使用
@Qualifier("beanName")限定 - 方案2:在字段名与Bean名一致时,
@Autowired自动按名称匹配 - 方案3:使用
@Primary标注首选Bean
示例:按名称注入的@Resource
【AOP实现原理】JDK动态代理 vs CGLIB代理
Spring AOP底层通过代理模式实现,根据目标对象是否实现接口选择代理方式:
Proxy实例② 实现
InvocationHandler接口③ 通过反射调用目标方法
④ 代理对象与目标对象实现相同接口
② 重写非final方法
③ 在方法前后织入增强逻辑
④ 注意:无法代理final类/方法
性能对比:
- 创建阶段:CGLIB > JDK(CGLIB需生成字节码)
- 运行阶段:JDK ≈ CGLIB(现代JDK已优化)
- 推荐策略:接口优先→JDK;无接口→CGLIB
源码线索:
Spring Boot高频面试题
【@SpringBootApplication】自动配置的底层实现
@SpringBootApplication是一个组合注解,核心包含:
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration |
等价于@Configuration,标识配置类 |
@EnableAutoConfiguration |
启用自动配置,核心是@Import(AutoConfigurationImportSelector.class) |
@ComponentScan |
扫描当前包及子包的组件 |
自动配置流程:
- 启动时扫描
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfigurationImportFilter - 读取
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件 - 加载配置类,按
@Conditional条件匹配(如@ConditionalOnClass) - 将符合条件的配置类注册为BeanDefinition
示例:spring.factories文件内容
【Starter】自定义Starter的实现步骤
创建自定义Starter需满足:自动配置 + 条件判断 + 外部化配置
@Configuration + @ConditionalOnClass + @EnableConfigurationProperties@ConfigurationProperties(prefix = "my.custom")META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports典型问题:如何避免Starter被重复加载?
- 使用
@ConditionalOnMissingBean确保仅当容器中无该Bean时才创建 - 通过
@AutoConfigureBefore/After控制加载顺序
【配置优先级】application.yml vs application.properties vs 命令行参数
| 配置源 | 优先级(高→低) | 示例 |
|---|---|---|
| 命令行参数 | 1 | --server.port=8081 |
| JNDI属性 | 2 | java:comp/env/spring.application.name |
| JVM系统属性 | 3 | -Dserver.port=8081 |
| 操作系统环境变量 | 4 | export SERVER_PORT=8081 |
| jar包外部的application.yml | 5 | config/application.yml |
| jar包内部的application.yml | 6 | BOOT-INF/classes/application.yml |
实践建议:
- 开发环境:使用
application-dev.yml+ 命令行参数覆盖 - 生产环境:通过环境变量注入敏感信息(如密码)
MyBatis高频面试题
【<choose> vs <if>】动态SQL的执行逻辑
MyBatis通过SqlSource解析动态标签,生成BoundSql对象:
执行过程:
XMLScriptBuilder解析XML,识别动态标签- 生成
DynamicSqlSource,内部持有SqlNode列表 - 运行时调用
getBoundSql,遍历SqlNode拼接SQL if:条件满足则保留节点内容choose:仅执行第一个满足条件的when,否则执行otherwise
常见陷阱:
test中使用OGNL表达式,注意空值判断(如name != null and name != '')- 避免
where 1=1硬编码,改用<where>标签自动处理AND/OR前缀
【插件Interceptor】如何实现分页插件?
MyBatis插件基于Interceptor接口,通过动态代理拦截四大对象:
| 拦截对象 | 方法 | 典型用途 |
|---|---|---|
Executor |
query(), update() |
SQL执行拦截、性能统计 |
StatementHandler |
prepare(), parameterize() |
SQL改写、分页重写 |
ParameterHandler |
setParameter() |
参数处理增强 |
ResultSetHandler |
handleResultSets() |
结果集转换 |
分页插件实现逻辑:
- 拦截
StatementHandler.prepare()方法 - 从
BoundSql获取原始SQL - 根据数据库类型生成COUNT查询和分页SQL
- 替换
BoundSql并继续执行
示例:分页插件核心代码
【一级/二级缓存】缓存失效场景与配置
一级缓存(SqlSession级别):
- 默认开启,基于
PerpetualCache实现 - 同一
SqlSession中,相同参数的查询仅执行一次SQL - 失效条件:
clearCache()、增删改操作、手动关闭
二级缓存(Mapper级别):
- 需在Mapper XML中配置
<cache>标签 - 实体类实现
Serializable接口 - 仅对
select语句生效,flushCache="true"可手动清空
| 缓存类型 | 存储介质 | 适用场景 |
|---|---|---|
| L1缓存 | 内存(HashMap) | 单次会话内查询优化 |
| L2缓存 | 内存/Redis(需集成) | 跨会话共享数据 |
| 第三方缓存 | Redis/Ehcache | 分布式集群环境 |
实践建议:
- 读多写少的字典表适合开启L2缓存
- 高并发场景建议使用Redis缓存替代MyBatis原生L2
微服务高频面试题
【Eureka vs Nacos】服务注册中心对比
| 特性 | Eureka(Netflix) | Nacos(阿里) |
|---|---|---|
| 协议 | HTTP/DISCOVERY | HTTP/DNS |
| 一致性 | AP(高可用) | 支持CP/AP切换 |
| 健康检查 | 心跳(RENEW) | 主动探测+心跳 |
| 配置管理 | 需结合Spring Cloud Config | 原生支持配置中心 |
| 多集群支持 | Zone/Region | Namespace/Group |
服务发现原理:
- 服务启动时,向注册中心发送注册请求(
POST /nacos/v1/ns/instance) - 注册中心保存实例元数据(IP、端口、健康状态)
- 客户端定期拉取服务列表(默认每30秒)
- 客户端本地缓存服务列表,实现负载均衡
典型问题:如何解决服务实例状态不一致?
- Eureka:自我保护模式(网络分区时保留所有实例)
- Nacos:支持临时/持久实例区分(临时实例依赖心跳)
【配置动态刷新】Spring Cloud Config vs Nacos
两种实现方式:
| 方式 | 原理 | 优缺点 |
|---|---|---|
| 客户端轮询 | @RefreshScope + /actuator/refresh |
实时性差,需手动触发 |
| 事件驱动 | Nacos监听配置变更 → 发布EnvironmentChangeEvent |
实时性高,推荐生产使用 |
配置刷新流程:
Environment@RefreshScope的Bean【Sentinel熔断规则】热点参数限流 vs 系统自适应
核心规则类型:
| 规则 | 适用场景 | 参数说明 |
|---|---|---|
| 流控规则 | 接口QPS控制 | count: 限流阈值;grade: QPS/线程数 |
| 降级规则 | 慢调用/异常比例 | slowRatioThreshold: 慢调用比例阈值 |
| 热点参数限流 | 热点数据保护 | paramIdx: 热点参数索引;burstCount: 突发流量 |
| 系统保护 | 全局负载均衡 | loadThreshold: 系统负载阈值 |
热点参数限流示例:
实战建议:
- 核心接口:设置热点参数限流(防刷单)
- 低优先级服务:开启系统自适应保护
性能调优与故障排查
【CPU 100%排查】Java服务高CPU占用的定位步骤
top -c → 找到高CPU进程PIDtop -Hp PID → 获取高CPU线程TIDprintf "%x\n" TID → 转为16进制(如0x1a2b)jstack PID | grep 0x1a2b -A 20典型场景:
- 死循环:线程栈持续在循环代码处
- 频繁GC:线程栈显示GC线程活跃
- 锁竞争:线程状态为
BLOCKED
【内存泄漏定位】如何判断是否存在内存泄漏?
诊断步骤:
- 监控指标:通过
jstat -gc PID 1000观察GC频率/时间 - 堆转储:使用
jmap -dump:live,format=b,file=heap.hprof PID - 分析工具:用
Eclipse MAT或jvisualvm打开堆文件 - 定位泄漏点:查看
Dominator Tree中占用内存最大的对象
常见泄漏场景:
| 场景 | 原因 | 解决方案 |
|---|---|---|
| 静态集合 | 静态Map持续增长 | 改用WeakReference或定期清理 |
| 未关闭资源 | IO流/数据库连接未close | 使用try-with-resources |
| 监听器未注销 | 事件监听器注册后未移除 | 生命周期结束时注销 |
职业发展与技术路线
【技术成长路径】Java工程师的3年规划
• 熟悉MySQL索引优化
• 了解Linux常用命令
• 能独立完成CRUD模块
• 掌握Redis缓存策略
• 学习微服务架构设计
• 参与高并发项目优化
• 建立质量保障体系
• 推动自动化运维
• 输出技术文档/培训
面试加分项:
- 开源贡献:GitHub有高质量项目
- 技术博客:持续输出深度文章
- 工具开发:内部效率工具开发者
【如何准备技术面试】结构化准备方法
“面试不是背答案,而是展示解决问题的思维过程”——某互联网公司技术面试官
三步准备法:
- 知识梳理:用思维导图整理核心知识点(如Spring生命周期图)
- 场景模拟:自问自答高频问题,录音后复盘
- 项目包装:用STAR法则(Situation-Task-Action-Result)重构项目经历
常见错误:
- 过度背诵答案,缺乏应变能力
- 项目描述过于笼统(“负责后端开发”)
- 回避技术难点(“当时没遇到问题”)
网友还关心:Java框架面试题相关问题
-
Java框架面试题中高频问到的源码题有哪些?
Spring:BeanPostProcessor执行时机;MyBatis:Plugin代理链生成;JVM:GC日志解读。建议结合源码注释+时序图理解。
-
如何准备微服务架构设计题?
从“选型依据→架构图→容灾方案”三步展开。例如:选Nacos因支持CP/AP切换;熔断用Sentinel的实时监控。
-
应届生如何回答“为什么选Java后端”?
结合技术热情+项目经历。例如:“在XX项目中通过优化MyBatis插件提升查询性能30%,激发对框架底层的兴趣”。
-
Java框架面试题中容易被忽略的细节?
如
@Transactional的 propagation=REQUIRES_NEW 与嵌套事务的关系;@Cacheable的key生成策略。