Spring Boot 启动与 Service 注入的 JVM 运行细节


Spring Boot 启动与 Service 注入的 JVM 运行细节

我们通过一个具体的 Spring Boot 项目示例,结合 JVM 的类加载和对象实例化机制,详细说明 Service 类的加载、Bean 的创建 以及 方法调用的时序关系


1. 示例代码
1.1 定义一个 Service 类
java 复制代码
@Service
public class UserService {
    // 静态代码块:类加载时执行(仅一次)
    static {
        System.out.println("[JVM] UserService 类加载,静态代码块执行");
    }

    // 构造方法:对象实例化时执行(每次创建 Bean 时执行)
    public UserService() {
        System.out.println("[Spring] UserService Bean 实例化,构造方法执行");
    }

    public void getUser() {
        System.out.println("[业务] 调用 getUser 方法");
    }
}
1.2 主启动类
java 复制代码
@SpringBootApplication
public class MyApp implements CommandLineRunner {
    @Autowired
    private UserService userService;

    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }

    @Override
    public void run(String... args) {
        System.out.println("[Spring] 应用启动完成,开始调用 UserService 方法");
        userService.getUser();
    }
}

2. 运行流程与输出分析
2.1 启动 Spring Boot 应用

运行 main 方法时,Spring Boot 启动流程如下:

  1. JVM 加载 MyApp

    • 加载 MyApp.class,生成 Class 对象。
    • 执行 MyApp<clinit>(如果有静态代码块)。
  2. Spring 容器初始化

    • 扫描 @SpringBootApplication 注解下的所有组件。
    • 加载 UserService
      • JVM 加载 UserService.class,触发静态代码块。
      • 输出:[JVM] UserService 类加载,静态代码块执行
  3. 创建 UserService Bean

    • Spring 实例化 UserService(单例模式,默认在启动时创建)。
    • 执行 UserService 的构造方法。
    • 输出:[Spring] UserService Bean 实例化,构造方法执行
  4. 依赖注入

    • UserService Bean 注入到 MyAppuserService 字段。
  5. 启动完成,调用 run 方法

    • 输出:[Spring] 应用启动完成,开始调用 UserService 方法
    • 调用 userService.getUser()
    • 输出:[业务] 调用 getUser 方法

2.2 完整输出结果
复制代码
[JVM] UserService 类加载,静态代码块执行
[Spring] UserService Bean 实例化,构造方法执行
...
[Spring] 应用启动完成,开始调用 UserService 方法
[业务] 调用 getUser 方法

3. 关键机制详解
3.1 类加载与静态代码块
  • 时机 :当 JVM 首次使用 UserService 类时(Spring 扫描到 @Service 注解时触发加载)。
  • 特点
    • 静态代码块在类加载的 初始化阶段 执行,且仅执行一次。
    • 与 Spring Bean 是否创建无关,仅依赖类是否被 JVM 加载。
3.2 Bean 实例化与构造方法
  • 时机 :Spring 容器启动时,默认 立即创建单例 Bean (可通过 @Lazy 改为延迟初始化)。
  • 特点
    • 构造方法在对象实例化时执行,每次创建 Bean 都会调用(单例模式下仅一次)。
    • Bean 的创建在类加载完成后进行。
3.3 方法调用
  • 时机:Bean 实例化完成后,通过依赖注入的实例调用方法。
  • 特点
    • 方法调用与对象生命周期无关,仅操作已存在的 Bean 实例。
    • 不会触发类加载或对象创建(单例模式下)。

4. 扩展:JVM 与 Spring 的协作流程
复制代码
+---------------------+       +---------------------+       +---------------------+
|     JVM 类加载阶段     |       |  Spring Bean 生命周期  |       |      业务方法调用       |
|---------------------|       |---------------------|       |---------------------|
| 1. 加载 MyApp.class  |       | 1. 扫描组件,加载类     |       | 1. 调用 userService.getUser() |
| 2. 加载 UserService  |       | 2. 实例化 Bean(构造方法)|       |                     |
|    - 执行静态代码块    |       | 3. 依赖注入           |       |                     |
+---------------------+       | 4. BeanPostProcessor |       +---------------------+
                               +---------------------+

5. 总结
  • 类加载:由 JVM 在 Spring 扫描组件时触发,静态代码块在此阶段执行(仅一次)。
  • Bean 实例化:由 Spring 容器在启动时完成,构造方法在此阶段执行(单例模式下仅一次)。
  • 方法调用:操作已存在的 Bean 实例,与 JVM 类加载和对象创建无关。

关键结论

  • Service 类在 Spring Boot 启动时被加载,构造方法在 Bean 实例化时执行
  • 调用 Service 方法时,使用的是已创建好的 Bean 对象,不会触发新的对象创建

通过此示例,可以清晰看到 JVM 类加载、Spring Bean 生命周期和业务方法调用之间的关系,帮助理解 Java 应用底层运行机制。

相关推荐
画个大饼32 分钟前
Go语言实战:快速搭建完整的用户认证系统
开发语言·后端·golang
oioihoii5 小时前
C++23 中 static_assert 和 if constexpr 的窄化布尔转换
java·jvm·c++23
李白的粉5 小时前
基于springboot的在线教育系统
java·spring boot·毕业设计·课程设计·在线教育系统·源代码
小马爱打代码6 小时前
SpringBoot原生实现分布式MapReduce计算
spring boot·分布式·mapreduce
iuyou️6 小时前
Spring Boot知识点详解
java·spring boot·后端
一弓虽6 小时前
SpringBoot 学习
java·spring boot·后端·学习
姑苏洛言6 小时前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
光而不耀@lgy7 小时前
C++初登门槛
linux·开发语言·网络·c++·后端
方圆想当图灵7 小时前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航7 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis