第一轮:基础不牢,地动山摇
面试官(推了推眼镜):谢飞机,先来聊聊你的项目吧,用过Spring Boot吗?
谢飞机(自信满满):当然!我用它写了个“用户登录系统”,就是配置个application.yml,加个@RestController,再写个@PathVariable,搞定!
面试官(点头):不错,有基本认知。那你知道Spring Boot自动配置的原理吗?
谢飞机(挠头):呃……好像是有个@SpringBootApplication注解,它会自动扫描包下面的类?
面试官:对,但更深层的是通过@EnableAutoConfiguration和spring.factories实现的。你了解@ConditionalOnClass和@ConditionalOnMissingBean吗?
谢飞机(眼神飘忽):嗯……条件判断?好像跟“有没有这个类”或“有没有这个Bean”有关吧?
面试官(轻叹):差不多。接着问,Spring Bean的生命周期是怎样的?
谢飞机:创建、初始化、使用、销毁……啊,还有前置后置处理器!比如InitializingBean和DisposableBean。
面试官(微笑):很好,还知道得挺清楚。那如果一个Bean同时实现了这两个接口,执行顺序是?
谢飞机:先执行InitializingBean,再执行DisposableBean?
面试官:错了,是先调用afterPropertiesSet(),然后才是destroy()方法。注意是“后置”处理,不是“销毁”才触发。
谢飞机(尴尬):哦……我记混了。
第二轮:多线程与并发,看真本事
面试官(翻开简历):你写过线程池?说说你用的线程池参数是什么?
谢飞机:corePoolSize、maximumPoolSize、workQueue、threadFactory、handler……
面试官:很好。那如果任务队列满了,会发生什么?
谢飞机:嗯……会拒绝执行,然后调用拒绝策略?
面试官:对。那你用过哪几种拒绝策略?
谢飞机:有AbortPolicy、CallerRunsPolicy……还有……嗯……
面试官:还有DiscardPolicy和DiscardOldestPolicy。你有没有遇到过线程池被撑爆的情况?
谢飞机:有!有一次服务器卡死了,我一看线程数飙到1000+,赶紧重启服务……
面试官(皱眉):那你怎么排查?有没有用JVM监控工具?
谢飞机:没用……我直接看日志,找报错信息。
面试官:好的,我们来点难的。你说说HashMap在扩容时是怎么解决哈希冲突的?
谢飞机:哈希冲突?就是链表变红黑树?
面试官:对,但你了解扩容过程中的数据迁移吗?
谢飞机:呃……就是把旧数组的数据重新计算下标,放到新数组里?
面试官:没错。但你知道为什么扩容后链表长度超过8才会转为红黑树吗?
谢飞机:因为……红黑树效率高?
面试官:对,但更重要的是,当链表长度达到8且数组容量≥64时才会转红黑树,避免频繁转换。你有没有想过,如果链表太长,性能下降怎么办?
谢飞机:那就换数据库?
面试官(扶额):……
第三轮:架构与设计,看格局
面试官(合上笔记本):现在我们来聊点架构。你用过Dubbo吗?
谢飞机:用过!我用它做服务调用,注册中心用Zookeeper,服务暴露用@Service,调用用@Reference。
面试官:很好。那你知道Dubbo的SPI机制是干嘛的吗?
谢飞机:是……插件式扩展?比如可以动态加载协议实现?
面试官:对。那你知道Dubbo是如何通过ExtensionLoader加载扩展的吗?
谢飞机:嗯……好像是读取META-INF/dubbo目录下的文件?
面试官:准确。接下来,你用过RabbitMQ吗?如何保证消息不丢失?
谢飞机:生产者发消息,消费者确认,然后设置持久化,还有死信队列……
面试官:很好。那如果消费者宕机,消息会不会丢失?
谢飞机:不会!因为消息是持久化的,等它重启就继续消费了。
面试官:如果消息已经投递但还没确认,消费者宕机了呢?
谢飞机:那……可能就丢了?
面试官:所以要开启事务,或者用confirm模式。最后问你个设计题:如果让你设计一个秒杀系统,你会怎么设计?
谢飞机:用Redis做库存,加锁,限流,然后用MQ异步下单……
面试官:不错。但你考虑过超卖问题吗?
谢飞机:……我让库存减到负数,再扣回来?
面试官(无奈):……好吧,你回去等通知吧。我们后续会联系你。
谢飞机(松了一口气):谢谢面试官,我明天还能来吗?
面试官:……不用了,回去好好学习吧。