第16章 面试真题解析

面试重要程度:⭐⭐⭐⭐⭐

常见提问方式:技术深度挖掘、项目细节追问、场景设计题

预计阅读时间:45分钟

开场白

兄弟,真题解析绝对是面试准备的重中之重!我收集了近两年各大厂的面试真题,从阿里、腾讯到字节、美团,每一道题都是面试官精心设计的。这些题目不仅考察你的技术深度,更能看出你的思维方式和解决问题的能力。

今天我们就把这些高频真题彻底搞透,让你在面试中游刃有余。

🏢 16.1 阿里巴巴面试真题

技术深度类

真题1:Spring Boot自动配置原理

面试官:"Spring Boot是如何实现自动配置的?@EnableAutoConfiguration注解做了什么?"

标准答案:

// 1. @EnableAutoConfiguration注解分析
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // ...
}

// 2. AutoConfigurationImportSelector核心逻辑
public class AutoConfigurationImportSelector implements DeferredImportSelector {
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        
        // 获取自动配置类
        AutoConfigurationEntry autoConfigurationEntry = 
            getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    
    protected AutoConfigurationEntry getAutoConfigurationEntry(
            AnnotationMetadata annotationMetadata) {
        
        // 1. 加载spring.factories文件中的配置类
        List<String> configurations = getCandidateConfigurations(
            annotationMetadata, attributes);
        
        // 2. 去重
        configurations = removeDuplicates(configurations);
        
        // 3. 排除不需要的配置
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        configurations.removeAll(exclusions);
        
        // 4. 过滤(根据条件注解)
        configurations = getConfigurationClassFilter().filter(configurations);
        
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

// 3. 条件注解示例
@Configuration
@ConditionalOnClass(DataSource.class)  // 类路径存在DataSource时生效
@ConditionalOnMissingBean(DataSource.class)  // 容器中没有DataSource时生效
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnProperty(prefix = "spring.datasource", name = "url")
    public DataSource dataSource(DataSourceProperties properties) {
        return DataSourceBuilder.create()
            .url(properties.getUrl())
            .username(properties.getUsername())
            .password(properties.getPassword())
            .build();
    }
}

面试加分回答:

"Spring Boot自动配置的核心原理是:

1. 启动类的@SpringBootApplication包含@EnableAutoConfiguration
2. @EnableAutoConfiguration通过@Import导入AutoConfigurationImportSelector
3. AutoConfigurationImportSelector读取META-INF/spring.factories文件
4. 根据条件注解(@ConditionalOnClass等)过滤配置类
5. 最终将符合条件的配置类注册到Spring容器

这种设计的优势是:
- 约定大于配置,减少样板代码
- 条件化配置,只加载需要的组件
- 可扩展性强,第三方可以提供自己的starter

我在项目中也基于这个原理开发了自定义starter..."

真题2:MySQL索引优化实战

面试官:"有一个用户表,字段包括id、name、age、city、create_time,
现在有个查询:SELECT * FROM user WHERE age > 25 AND city = '北京' AND create_time > '2023-01-01'
如何优化这个查询?"

标准答案:

-- 1. 分析查询条件
-- age > 25 (范围查询)
-- city = '北京' (等值查询)  
-- create_time > '2023-01-01' (范围查询)

-- 2. 索引设计原则:等值查询在前,范围查询在后
-- 最优索引:(city, age, create_time) 或 (city, create_time, age)

-- 创建复合索引
CREATE INDEX idx_city_age_createtime ON user(city, age, create_time);

-- 3. 查询优化
-- 原查询
SELECT * FROM user 
WHERE age > 25 AND city = '北京' AND create_time > '2023-01-01';

-- 优化后(如果不需要所有字段)
SELECT id, name, age, city 
FROM user 
WHERE city = '北京' AND age > 25 AND create_time > '2023-01-01';

-- 4. 使用EXPLAIN分析执行计划
EXPLAIN SELECT * FROM user 
WHERE city = '北京' AND age > 25 AND create_time > '2023-01-01';

-- 5. 进一步优化:覆盖索引
CREATE INDEX idx_city_age_createtime_covering 
ON user(city, age, create_time, id, name);

面试加分回答:

"这个查询优化我会从几个方面考虑:

1. 索引设计:
   - 根据查询条件创建复合索引(city, age, create_time)
   - city是等值查询,放在最前面,选择性好
   - age和create_time都是范围查询,顺序可以根据数据分布调整

2. 查询重写:
   - 避免SELECT *,只查询需要的字段
   - 考虑使用覆盖索引,避免回表查询

3. 数据分布分析:
   - 如果city的选择性不好,考虑调整索引顺序
   - 可以收集统计信息,让优化器选择最优执行计划

4. 分区优化:
   - 如果数据量很大,可以考虑按create_time分区
   - 这样可以减少扫描的数据量

在我之前的项目中,类似的优化让查询性能提升了10倍..."

系统设计类

真题3:设计一个秒杀系统

面试官:"双11秒杀,1000万用户抢100个商品,如何设计这个系统?"

标准答案:

// 1. 系统架构设计
/*
前端 -> CDN -> 负载均衡 -> 网关 -> 秒杀服务 -> 缓存 -> 数据库
                                    ↓
                                 消息队列
                                    ↓
                                 订单服务
*/

// 2. 核心秒杀服务
@RestController
@RequestMapping("/seckill")
public class SeckillController {
    
    @Autowired
    private SeckillService seckillService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 秒杀接口
    @PostMapping("/kill/{productId}")
    @RateLimiter(rate = 1000) // 限流
    public Result<String> seckill(@PathVariable Long productId, 
                                 @RequestParam Long userId) {
        
        // 1. 参数校验
        if (productId == null || userId == null) {
            return Result.fail("参数错误");
        }
        
        // 2. 用户重复购买检查
        String userKey = "seckill:user:" + productId + ":" + userId;
        if (redisTemplate.hasKey(userKey)) {
            return Result.fail("您已经参与过该商品的秒杀");
        }
        
        // 3. 库存预检查
        String stockKey = "seckill:stock:" + productId;
        Long stock = redisTemplate.opsForValue().decrement(stockKey);
        
        if (stock < 0) {
            // 库存不足,恢复库存
            redisTemplate.opsForValue().increment(stockKey);
            return Result.fail("商品已售罄");
        }
        
        // 4. 异步处理订单
        SeckillMessage message = new SeckillMessage(productId, userId);
        rabbitTemplate.convertAndSend("seckill.queue", message);
        
        // 5. 设置用户参与标记
        redisTemplate.opsForValue().set(userKey, "1", 3600, TimeUnit.SECONDS);
        
        return Result.success("秒杀请求提交成功,请稍后查看结果");
    }
}

// 3. 异步订单处理
@RabbitListener(queues = "seckill.queue")
@Component
public class SeckillOrderProcessor {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void processSeckillOrder(SeckillMessage message) {
        try {
            // 1. 再次检查库存(防止超卖)
            String stockKey = "seckill:stock:" + message.getProductId();
            String lockKey = "seckill:lock:" + message.getProductId();
            
            Boolean lockAcquired = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
            
            if (!lockAcquired) {
                // 获取锁失败,重新入队
                rabbitTemplate.convertAndSend("seckill.queue", message);
                return;
            }
            
            try {
                // 2. 检查数据库真实库存
                Product product = productService.getById(message.getProductId());
                if (product.getStock() <= 0) {
                    // 库存不足,恢复Redis库存
                    redisTemplate.opsForValue().increment(stockKey);
                    return;
                }
                
                // 3. 创建订单
                Order order = new Order();
                order.setUserId(message.getUserId());
                order.setProductId(message.getProductId());
                order.setStatus(OrderStatus.UNPAID);
                order.setCreateTime(new Date());
                
                // 4. 扣减库存并创建订单(事务)
                orderService.createSeckillOrder(order, message.getProductId());
                
                // 5. 发送订单创建成功消息
                notificationService.sendOrderSuccess(message.getUserId(), order.getId());
                
            } finally {
                redisTemplate.delete(lockKey);
            }
            
        } catch (Exception e) {
            log.error("处理秒杀订单失败", e);
            // 恢复库存
            String stockKey = "seckill:stock:" + message.getProductId();
            redisTemplate.opsForValue().increment(stockKey);
        }
    }
}

// 4. 库存预热
@Component
public class SeckillStockWarmer {
    
    @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨执行
    public void warmUpStock() {
        List<SeckillProduct> products = seckillProductService.getTodayProducts();
        
        for (SeckillProduct product : products) {
            String stockKey = "seckill:stock:" + product.getId();
            redisTemplate.opsForValue().set(stockKey, product.getStock());
            
            // 设置过期时间
            redisTemplate.expire(stockKey, 24, TimeUnit.HOURS);
        }
    }
}

面试加分回答:

"秒杀系统的核心挑战是高并发和超卖问题,我的设计思路:

1. 前端优化:
   - 静态资源CDN加速
   - 按钮防重复点击
   - 验证码防刷

2. 接入层:
   - 负载均衡分散流量
   - 网关层限流熔断
   - IP黑白名单

3. 业务层:
   - Redis预扣库存,快速响应
   - 异步处理订单,削峰填谷
   - 分布式锁防止超卖

4. 数据层:
   - 读写分离
   - 数据库连接池优化
   - 事务最小化

5. 监控告警:
   - 实时监控QPS、响应时间
   - 库存监控和告警
   - 异常订单处理

这套方案在我们的项目中支撑了千万级并发..."

🐧 16.2 腾讯面试真题

并发编程类

真题4:线程池参数调优

面试官:"线程池的核心参数有哪些?在生产环境中如何调优?"

标准答案:

// 1. 线程池核心参数
public ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数
    int maximumPoolSize,   // 最大线程数
    long keepAliveTime,    // 线程存活时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue,  // 工作队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
) {
    // ...
}

// 2. 生产环境线程池配置
@Configuration
public class ThreadPoolConfig {
    
    // CPU密集型任务线程池
    @Bean("cpuTaskExecutor")
    public ThreadPoolExecutor cpuTaskExecutor() {
        int coreSize = Runtime.getRuntime().availableProcessors();
        
        return new ThreadPoolExecutor(
            coreSize,                    // 核心线程数 = CPU核数
            coreSize * 2,               // 最大线程数 = CPU核数 * 2
            60L,                        // 空闲线程存活60秒
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(100),  // 有界队列,防止OOM
            new CustomThreadFactory("cpu-task-"),
            new ThreadPoolExecutor.CallerRunsPolicy()  // 调用者执行策略
        );
    }
    
    // IO密集型任务线程池
    @Bean("ioTaskExecutor")
    public ThreadPoolExecutor ioTaskExecutor() {
        int coreSize = Runtime.getRuntime().availableProcessors() * 2;
        
        return new ThreadPoolExecutor(
            coreSize,                    // 核心线程数 = CPU核数 * 2
            coreSize * 4,               // 最大线程数 = CPU核数 * 4
            60L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(200),
            new CustomThreadFactory("io-task-"),
            new ThreadPoolExecutor.AbortPolicy()  // 抛异常策略
        );
    }
    
    // 自定义线程工厂
    public static class CustomThreadFactory implements ThreadFactory {
        private final AtomicInteger threadNumber = new AtomicInteger(1);

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务