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 应用底层运行机制。

相关推荐
战族狼魂26 分钟前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
xyliiiiiL1 小时前
ZGC初步了解
java·jvm·算法
杉之2 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch2 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
bobz9653 小时前
k8s 怎么提供虚拟机更好
后端
bobz9653 小时前
nova compute 如何创建 ovs 端口
后端
用键盘当武器的秋刀鱼4 小时前
springBoot统一响应类型3.5.1版本
java·spring boot·后端
Asthenia04124 小时前
从迷宫到公式:为 NFA 构造正规式
后端
小李同学_LHY4 小时前
三.微服务架构中的精妙设计:服务注册/服务发现-Eureka
java·spring boot·spring·springcloud
Asthenia04125 小时前
像整理玩具一样:DFA 化简和状态等价性
后端