阿里淘天 Java 后端一面面经
1. 先做个自我介绍,再重点讲一个你参与最深的项目模块。
答:
这个问题几乎是必问,面试官主要不是听你“讲故事”,而是看你表达是否清晰、项目是不是你真的做过。比较好的回答方式是按这几个点展开:
- 个人背景:毕业时间、工作年限、当前技术栈。
- 项目背景:这个项目解决什么业务问题,面向谁。
- 自己负责什么:不要只说“参与了开发”,而要明确到模块,比如订单、库存、营销、支付、搜索推荐等。
- 技术细节:用了哪些中间件、数据库、缓存、消息队列。
- 难点和结果:遇到过什么问题,怎么解决,最后指标提升了多少。
比如可以这样答:
“我主要做 Java 后端开发,平时技术栈是 Spring Boot、MySQL、Redis、MQ。最近做得比较深的是一个订单履约模块,我主要负责订单状态流转、库存扣减和异步通知这一块。这个模块的难点主要在高并发下的数据一致性和接口响应时间优化,后来我们通过缓存、异步解耦和 SQL 优化,把接口 RT 从 300ms 降到了 100ms 左右,同时把库存超卖问题控制住了。”
这个问题回答好,后面很多追问都会围绕你项目展开,所以建议提前准备。
2. MySQL 联合索引为什么要遵循最左前缀匹配原则?
答:
联合索引本质上是按照多个字段组合建立出来的有序结构,比如 (a, b, c)。它在底层排序时,先按 a 排,再按 b 排,最后按 c 排。
因此,查询时如果想用上这个索引,必须从最左边开始连续匹配。
例如索引是 (a, b, c):
where a = 1:可以用where a = 1 and b = 2:可以用where a = 1 and b = 2 and c = 3:可以用where b = 2 and c = 3:通常不能直接用完整联合索引where a = 1 and c = 3:一般只能用到a
原因是底层 B+ 树先按最左列组织,如果跳过了最左列,后面的有序性就无法充分利用。
所以建联合索引时,一般要把区分度高、过滤能力强、最常用作查询条件的字段放前面。
3. explain 里面常见的几个字段你怎么理解?怎么判断 SQL 有没有走好索引?
答:
explain 是排查 SQL 性能的常用工具,面试里经常问。几个重点字段可以这样理解:
type:访问类型,性能从好到差大致是system > const > eq_ref > ref > range > index > all。 如果看到all,通常说明全表扫描,性能较差。key:实际使用的索引。rows:MySQL 预估要扫描的行数,越少越好。Extra:额外信息。 常见的有: Using index:说明使用了覆盖索引,比较好Using where:说明还需要做条件过滤Using filesort:说明排序没利用索引,可能性能差Using temporary:说明用了临时表,通常也要关注
判断 SQL 是否优化得比较好,一般看几个点:
- 有没有命中合适索引
type至少不要太差- 扫描行数是不是明显过多
- 有没有
filesort、temporary - 是否存在回表过多的问题
4. 什么是索引下推?它解决了什么问题?
答:
索引下推(ICP,Index Condition Pushdown)可以理解为:在索引遍历阶段就尽可能多做条件过滤,减少回表次数。
比如有联合索引 (name, age),SQL 是:
select * from user where name like 'zhang%' and age = 20;
如果没有索引下推,MySQL 可能先根据 name 找到一批记录,然后一条条回表,再判断 age = 20。
有了索引下推后,在索引层就能先过滤 age = 20,只有满足条件的才回表,这样能减少很多无效 IO。
它的核心价值就是:
减少回表,减少磁盘访问,提高查询效率。
5. Spring 中 Bean 是如何被创建出来的?大致流程是什么?
答:
这个问题本质上是在问 Spring IOC 容器启动和 Bean 创建流程。可以按顺序回答:
- 读取配置Spring 启动时会先读取 XML、注解、Java Config 等配置信息,把这些定义解析成 BeanDefinition。BeanDefinition 可以理解为 Bean 的“配方”,里面描述了类名、作用域、依赖、初始化方法等信息。
- 实例化 Bean容器根据 BeanDefinition 反射创建对象。如果是构造器注入,这一步会先选择合适的构造方法。
- 属性注入把依赖对象注入到 Bean 中,比如 @Autowired、@Resource 标注的属性或方法。
- Aware 回调如果 Bean 实现了 BeanNameAware、BeanFactoryAware、ApplicationContextAware 等接口,Spring 会回调这些方法,把容器相关信息传进去。
- BeanPostProcessor 前置处理初始化前执行 postProcessBeforeInitialization()。
- 初始化执行 InitializingBean.afterPropertiesSet() 或自定义 init-method。
- BeanPostProcessor 后置处理初始化后执行 postProcessAfterInitialization()。AOP 代理对象很多就是在这个阶段生成的。
- 放入单例池如果是单例 Bean,会放到单例缓存里,后面直接复用。
整体上,Spring IOC 的核心思想就是:
对象的创建、依赖管理、生命周期都交给容器统一管理。
6. Spring 事务底层是怎么实现的?为什么有时候会失效?
答:
Spring 事务本质上是通过 AOP + 代理 实现的。
当你在方法上加 @Transactional 时,Spring 并不是直接修改这个方法,而是为这个 Bean 创建一个代理对象。
调用这个代理对象的方法时,会先进入事务拦截逻辑:
- 开启事务
- 执行目标方法
- 如果正常结束就提交事务
- 如果抛出符合规则的异常就回滚事务
常见事务失效场景有这些:
- 同类自调用一个类里的方法 A 调用本类另一个加了事务的方法 B,通常事务不生效。因为这不是通过代理对象调用,而是对象内部直接调用。
- 方法不是 public默认情况下,很多事务代理只拦截 public 方法。
- 异常被吞掉如果方法内部捕获了异常却不继续抛出,Spring 感知不到异常,就不会回滚。
- 抛出的不是默认回滚异常默认只对 RuntimeException 和 Error 回滚。如果抛的是受检异常,可能要显式指定 rollbackFor。
- 没有被 Spring 管理如果对象不是 Spring 容器里的 Bean,事务注解也不会生效。
面试回答时,关键是强调:
S
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经,带你练透java圣经
查看7道真题和解析