Java全栈面试宝典:线程机制与Spring依赖注入深度解析

发布于:2025-03-30 ⋅ 阅读:(41) ⋅ 点赞:(0)

目录

一、Java线程核心机制

🔥 问题3:start()与run()的底层执行差异

线程启动流程图解

核心差异对照表

代码验证示例

🔥 问题4:Thread与Runnable的六大维度对比

类关系UML图

最佳实践代码

🔥 问题5:线程参数传递三大方案

参数传递方案对比

Lambda传参最佳实践

二、Spring依赖注入全景解析

🌟 Spring依赖注入四大方式

DI实现方式对比表

构造器注入最佳实践

🌟 Spring自动装配原理图解

自动装配注解对比

三、高频面试题强化训练

1. Spring为什么推荐构造器注入?

2. @Autowired与@Resource的区别?

3. 如何解决多个同类型Bean的冲突?


一、Java线程核心机制

🔥 问题3:start()与run()的底层执行差异

线程启动流程图解


核心差异对照表
方法 执行线程 调用次数 JVM操作 典型应用场景
start() 新创建的子线程 单次 触发native线程创建 多线程任务启动
run() 主调用线程 多次 普通Java方法调用 单线程测试/调试
代码验证示例
public class StartVsRun {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("当前线程: " + Thread.currentThread().getName());
        });
        
        thread.start();  // 输出: Thread-0
        thread.run();    // 输出: main
    }
}

🔥 问题4:Thread与Runnable的六大维度对比

类关系UML图

维度 Thread Runnable
类型 具体类 接口
继承限制 占用继承名额 不占用
资源共享 实例变量独立 可共享同一实例
扩展性 单继承限制 支持多实现
线程池兼容性 需包装为Runnable 直接支持
设计模式 具体实现 策略模式
最佳实践代码
// 推荐实现方式
public class DownloadTask implements Runnable {
    private String url;
    
    public DownloadTask(String url) {
        this.url = url;
    }
    
    @Override
    public void run() {
        // 文件下载逻辑
    }
}

// 使用示例
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(new DownloadTask("https://example.com/file1"));
pool.execute(new DownloadTask("https://example.com/file2"));

🔥 问题5:线程参数传递三大方案

参数传递方案对比
方式 实现示例 优点 缺点
构造器传参 new MyThread(param).start() 线程安全 创建后参数不可变
成员变量传参 thread.setConfig(config) 灵活修改 需处理线程可见性问题
回调函数传参 executor.submit(() -> task(param)) Lambda简洁 要求参数final或等效
Lambda传参最佳实践
public class ThreadParamDemo {
    public static void main(String[] args) {
        String config = "server.properties";
        
        // Lambda传参(实质是闭包)
        new Thread(() -> loadConfig(config)).start();
        
        // 通过Future传参
        ExecutorService pool = Executors.newSingleThreadExecutor();
        Future<String> future = pool.submit(() -> processData(config));
    }
    
    private static void loadConfig(String path) {
        // 配置文件加载逻辑
    }
    
    private static String processData(String input) {
        // 数据处理逻辑
        return "processed_" + input;
    }
}

二、Spring依赖注入全景解析

🌟 Spring依赖注入四大方式

DI实现方式对比表
注入方式 实现示例 优点 缺点
构造器注入 new Service(dao) 不可变/线程安全 参数多时代码冗长
Setter注入 service.setDao(dao) 灵活可选依赖 可能破坏不变性
字段注入 @Autowired private Dao dao 代码简洁 测试困难/隐藏依赖
方法注入 @Autowired public void init(Dao dao) 精确控制时机 使用频率较低
构造器注入最佳实践
@Service
public class OrderService {
    private final PaymentService paymentService;
    private final InventoryService inventoryService;
    
    @Autowired
    public OrderService(PaymentService paymentService, 
                       InventoryService inventoryService) {
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }
}

🌟 Spring自动装配原理图解

自动装配注解对比
注解 装配方式 适用场景 冲突解决
@Autowired 按类型优先 多数单实现场景 @Qualifier指定名称
@Resource 按名称优先 JNDI资源注入 名称不存在时按类型
@Inject 与@Autowired类似 JSR-330标准实现 需要额外依赖

三、高频面试题强化训练

1. Spring为什么推荐构造器注入?

  • 不可变性:确保依赖项在初始化后不变

  • 循环依赖检测:启动时立即发现依赖问题

  • 测试友好:便于通过构造器传递Mock对象

  • 线程安全:避免并发修改依赖项

2. @Autowired与@Resource的区别?

维度 @Autowired @Resource
标准归属 Spring专属 JSR-250标准
默认装配策略 按类型 按名称
必需性控制 required=false
参数指定方式 @Qualifier name属性
应用范围 字段/构造器/方法 字段/setter方法

3. 如何解决多个同类型Bean的冲突?

// 方案一:使用@Primary标记主候选
@Bean
@Primary
public DataSource masterDataSource() {
    return new HikariDataSource();
}

// 方案二:使用@Qualifier指定名称
@Autowired
@Qualifier("backupDataSource")
private DataSource dataSource;

// 方案三:使用自定义限定注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("report")
public @interface ReportDataSource {}

@Bean
@ReportDataSource
public DataSource reportDataSource() {
    return new DruidDataSource();
}

实战建议

  1. 在Spring Boot中通过@ConfigurationProperties实现类型安全配置注入

  2. 使用@Lazy注解延迟初始化资源密集型Bean

  3. 通过@Profile实现环境特定的Bean装配

💬 你在项目中更倾向于使用哪种依赖注入方式?遇到过哪些注入难题?
🎁 关注+转发,抽送《阿里Java开发手册》电子书