掌握Spring Boot启动流程:执行策略与关键注解详解

SpringBoot启动流程中的指定方法执行策略

执行顺序

  • 当Spring容器启动时,首先会进行依赖注入,这时候会执行标记有@Autowired的字段、构造函数或方法。
  • 依赖注入完成后,会执行标记有@PostConstruct的方法。
  • 当Spring容器关闭时,会先执行标记有@PreDestroy的方法。
  • 在应用程序启动后,会执行实现了ApplicationRunner或CommandLineRunner接口的类的run方法。
  • 如果有多个ApplicationRunner或CommandLineRunner,它们的执行顺序取决于它们在Spring容器中的注册顺序。
  • ApplicationListener接口的实现类会在特定的事件发生时被调用。

@PostConstruct

定义

@PostConstruct是 Java 自带的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在 Spring 容器初始化的时候执行该方法。可作为一些数据的常规化加载,比如数据字典之类的。

从 Java EE5 规范开始,Servlet 中增加了两个影响 Servlet 生命周期的注解,@PostConstruct@PreDestroy,这两个注解被用来修饰一个非静态的void()方法。

关于 @PostConstruct@Autowired、构造函数的执行顺序:
构造方法 > @Autowired > @PostConstruct

@PostConstruct 是 Java 的一种注解,由 JSR-250 定义,被 Spring 框架支持。它用于标记那些在依赖注入完成后需要立即执行的非构造初始化方法。这种注解使得开发者可以在对象创建后立即进行必要的初始化工作,而无需显式地调用方法。下面详细解释如何使用 @PostConstruct 和一些最佳实践。

基本使用

在 Spring 环境中,@PostConstruct 注解的方法会在以下时机执行:

  1. 在所有依赖注入完成后,但在 bean 被任何客户端代码使用之前。
  2. @Autowired 注解的字段或构造器初始化之后。
  3. 在任何 Aware 接口方法或 InitializingBean 接口的 afterPropertiesSet() 方法之前。

示例代码:

public class ExampleService {

    private String someValue;

    @Autowired
    public ExampleService(String someValue) {
        this.someValue = someValue;
    }

    @PostConstruct
    public void initialize() {
        System.out.println("Initialization logic goes here.");
        // 这里可以进行初始化逻辑,如连接数据库,加载配置等
    }
}

注意事项

  • 方法签名@PostConstruct 标注的方法必须没有参数,返回类型为 void,并且不能抛出已检查异常。
  • 方法调用次数:一个 @PostConstruct 标注的方法在整个 bean 生命周期中只调用一次。
  • 非静态方法:只能标注在实例方法上,不能用于静态方法。

性能考虑

如果 @PostConstruct 方法中包含大量耗时的初始化逻辑,可能会延迟 Spring 容器的启动。为了提高性能,可以采用以下策略:

  1. 异步执行:如果初始化操作非常耗时,可以考虑使用 ExecutorService 或者 Spring 的 @Async 注解来异步执行这些操作,从而避免阻塞 Spring 容器的初始化。
  2. 分离初始化逻辑:对于一些不是必须在应用启动时就完成的初始化工作,可以将其移到 CommandLineRunnerApplicationRunner 实现中,这样初始化操作就不会影响到应用的启动速度。

与 Spring 生命周期方法的关系

@PostConstruct 方法与 Spring 的初始化回调方法如 InitializingBeanafterPropertiesSet() 有一定的区别。@PostConstruct 方法由 JSR-250 规范定义,而 afterPropertiesSet() 是 Spring 特有的。两者都会在依赖注入完成后执行,但是 @PostConstruct 方法的执行优先于 afterPropertiesSet() 方法。

总之,@PostConstruct 提供了一种简单而有效的方式来执行依赖注入完成后的初始化逻辑,只要遵守其使用规则,它能极大地简化对象的初始化过程。

CommandLineRunner接口

CommandLineRunner是Spring Boot框架提供的一种机制,用于执行一些在应用启动完成后的初始化任务。这个接口非常简单,只有一个方法需要实现,即run方法。当一个实现了CommandLineRunner接口的bean被创建时,Spring容器会在所有bean初始化完成后调用它的run方法。

定义

public interface CommandLineRunner {

    /**
     * 在应用程序启动后运行此方法。
     * @param args 应用程序接收的命令行参数
     * @throws Exception 如果初始化过程中发生错误
     */
    void run(String... args) throws Exception;
}

CommandLineRunner的主要用途包括但不限于:

  • 执行数据库迁移或预加载数据到数据库。
  • 初始化缓存或执行其他一些启动前的准备工作。
  • 打印一些诊断信息或日志,确认应用程序已经成功启动。

基本使用

CommandLineRunner接口在Spring Boot应用中提供了一个简洁的方式,用于执行一些启动后的初始化任务,比如设置环境、加载数据、执行脚本或者打印欢迎信息等。以下是CommandLineRunner的详细使用说明和示例:

创建CommandLineRunner Bean

要在你的应用中使用CommandLineRunner,首先你需要创建一个实现该接口的类,并将其注册为一个Spring Bean。这可以通过在类上添加@Component注解来实现,这样Spring的组件扫描功能就能发现并管理它。

示例代码:

@Component
public class StartupInitializer implements CommandLineRunner {

    private final Environment env;

    public StartupInitializer(Environment env) {
        this.env = env;
    }

    @Override
    public void run(String... args) throws Exception {
        if (env.getActiveProfiles().length > 0) {
            System.out.println("Active Profiles: " + Arrays.toString(env.getActiveProfiles()));
        } else {
            System.out.println("No active profiles set.");
        }

        // 执行其他初始化任务,例如从数据库加载数据或设置缓存
        // ...

        // 打印一些诊断信息,确认应用启动情况
        System.out.println("Startup initialization complete.");
    }
}

在这个示例中,StartupInitializer类实现了CommandLineRunner接口,其构造函数接收了一个Environment类型的参数,用于获取应用的配置信息。run方法则用来执行一系列初始化任务,包括打印活动的配置文件名和一些诊断信息。

使用CommandLineRunner进行数据初始化

假设你有一个数据库,需要在应用启动时加载一些初始数据。你可以利用CommandLineRunner来完成这个任务:

@Component
public class DataInitializer implements CommandLineRunner {

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public DataInitializer(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void run(String... args) throws Exception {
        // 插入一些初始数据
        jdbcTemplate.update("INSERT INTO users (name, email) VALUES (?, ?)", "John Doe", "john.doe@example.com");
        System.out.println("Initial data loaded into the database.");
    }
}

在这个例子中,DataInitializer类通过JdbcTemplate向数据库插入初始数据。JdbcTemplate是通过构造函数注入的,这是Spring推荐的依赖注入方式之一。

注意事项

  • CommandLineRunnerrun方法会在所有bean初始化完成后调用,因此可以安全地访问其他bean。
  • 如果有多个CommandLineRunner bean,它们将按照定义的顺序执行,这个顺序可以通过实现Ordered接口或使用@Order注解来指定。
  • run方法可以抛出异常,如果抛出了未检查异常(即RuntimeException及其子类),Spring会处理这些异常;如果抛出的是已检查异常,则需要在方法签名中声明它们。

通过这种方式,CommandLineRunner提供了一个强大的工具,用于在Spring Boot应用启动后执行各种初始化任务,帮助确保你的应用在开始处理请求前处于预期的状态。

ApplicationRunner接口

ApplicationRunner是Spring Boot提供的一个接口,用于执行一些在应用启动后需要运行的任务。与CommandLineRunner类似,ApplicationRunner提供了一个执行入口点,但其主要区别在于ApplicationRunner可以访问ApplicationContext,这使得它可以访问更多的上下文信息。

定义

ApplicationRunner接口定义如下:

public interface ApplicationRunner {

    /**
     * 在应用上下文已经被刷新并可用后,但在调用 {@link ApplicationContextAware} 接口或发布
     * {@link ContextRefreshedEvent} 事件之前,运行此方法。
     *
     * @param args 应用程序接收到的命令行参数
     * @throws Exception 在运行时抛出的任何异常
     */
    void run(ApplicationArguments args) throws Exception;
}

基本使用

要使用ApplicationRunner,你需要创建一个实现该接口的类,并将其作为Spring Bean注册。这样,在应用启动后,Spring会调用其实例的run方法。

示例代码:

@Component
@Profile("dev")
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 访问应用上下文
        ApplicationContext context = ...; // 可以通过@Autowired注入

        // 执行一些初始化任务
        System.out.println("Application is starting up with arguments: " + args.getSourceArgs());

        // 其他初始化代码...
    }
}

注意事项

  1. 访问ApplicationContext

    • ApplicationRunner的一个主要优势是它能够访问ApplicationContext。这允许你执行诸如检索bean、访问配置属性或触发事件等操作。
  2. 参数访问

    • run方法接收一个ApplicationArguments参数,它包含了应用启动时传入的命令行参数。这对于需要根据命令行参数执行特定逻辑的情况很有用。
  3. 执行顺序

    • 如果有多个ApplicationRunner bean,它们的执行顺序是不确定的。如果需要控制执行顺序,可以实现Ordered接口或使用@Order注解。
  4. 异常处理

    • 如果run方法抛出异常,Spring会捕获并记录它,但不会阻止应用的正常启动。因此,你应该确保处理好可能抛出的任何异常,或者设计应用能够容忍这些异常。
  5. 与CommandLineRunner的区别

    • ApplicationRunnerCommandLineRunner提供了更多的功能,尤其是对ApplicationContext的访问。然而,如果你只需要简单地执行一些不需要上下文的代码,CommandLineRunner可能就足够了。

通过使用ApplicationRunner,你可以在应用启动后执行一系列的初始化任务,同时能够充分利用Spring的ApplicationContext,使其成为一个强大的工具来满足各种启动时的需求。

ApplicationListener接口

ApplicationListener是Spring框架中的一个接口,用于监听和响应在ApplicationContext中发布的特定事件。这个接口是实现事件驱动架构的关键组件之一,允许开发人员编写能够响应应用上下文中的状态变化的代码。

定义

ApplicationListener接口定义如下:

public interface ApplicationListener<E extends ApplicationEvent> {

    /**
     * 当事件发生时,这个方法会被调用。
     *
     * @param event 应用程序事件
     */
    void onApplicationEvent(E event);
}

这里的E是泛型参数,代表ApplicationEvent的子类型,这使得ApplicationListener可以专门监听某种类型的事件。

基本使用

要使用ApplicationListener,你需要做以下几步:

  1. 创建事件监听器
    创建一个类实现ApplicationListener接口,并指定你感兴趣的事件类型。

  2. 注册监听器
    将监听器注册到ApplicationContext中。这通常通过将监听器类声明为一个@Component来自动完成,或者手动在配置中注册。

示例代码:

@Component
public class StartupEventListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 这里是当上下文刷新完成时的处理逻辑
        System.out.println("Application context has been refreshed.");
    }
}

注意事项

  1. 事件类型

    • 确保你的监听器正确地指定了它所监听的事件类型。Spring框架提供了多种内置的ApplicationEvent子类型,如ContextRefreshedEventContextStartedEventContextStoppedEventContextClosedEvent
  2. 事件传播

    • 事件是在ApplicationContext层级上传播的,这意味着如果你的应用有多个ApplicationContext实例,事件将只在相应的上下文中传播。
  3. 事件的顺序性和并发性

    • 事件的处理顺序并不是保证的,特别是在多线程环境下。如果你需要按顺序处理事件,可能需要在监听器内部实现额外的同步机制。
    • 同样,如果你的事件处理器可能被多个线程同时调用,确保你的代码是线程安全的。
  4. 资源消耗

    • 大量的事件监听器可能会对应用性能造成影响,尤其是在事件频繁触发的情况下。确保优化你的事件处理器,避免不必要的资源消耗。
  5. 事件的生命周期

    • 监听器的onApplicationEvent方法会在事件发生时被调用,但并不意味着它会在事件发生的那一刻立即调用。具体何时调用取决于事件队列的处理机制。

通过使用ApplicationListener,你可以构建响应式和事件驱动的系统,这些系统能够在状态变化时做出反应,无论是应用启动、关闭还是其他自定义事件。这增加了系统的灵活性和可扩展性,是Spring框架强大功能的重要组成部分。

总结

@PostConstruct

  • 这个注解用于标记方法,该方法会在依赖注入完成后执行。它是一个JSR-250规范的注解,因此它不仅仅可以在Spring中使用,还可以在任何支持JSR-250的容器中使用。
  • 这个方法通常用于执行一些初始化操作,比如打开数据库连接或者设置一些资源。

@Autowired

  • 这个注解用于自动装配Spring容器中的bean。当你在一个字段、构造函数、setter方法或者任何其他方法上使用@Autowired时,Spring容器会在创建bean的时候自动注入相应的依赖。
  • 这个注解是Spring框架中非常重要的一个特性,它大大简化了代码的编写,使得开发者可以专注于业务逻辑。

@PreDestroy

  • 类似于@PostConstruct,@PreDestroy也是一个JSR-250注解,用于标记在bean销毁之前执行的方法。
  • 这个方法可以用来执行一些清理工作,比如关闭数据库连接或者清理临时文件。

ApplicationListener

  • ApplicationListener是一个接口,实现了这个接口的类可以监听和响应Spring应用程序中的各种事件。
  • 通过实现这个接口,你可以自定义事件监听器来处理特定类型的事件,比如上下文初始化、上下文刷新、上下文关闭等。

ApplicationRunner

  • 这个接口包含一个run方法,实现了这个接口的类可以在Spring应用程序启动后执行一些代码。
  • 它通常用于执行一些启动后的任务,比如初始化某些系统资源或者执行一些启动检查。

CommandLineRunner

  • 类似于ApplicationRunner,CommandLineRunner也是一个接口,但它专门用于Spring Boot应用程序。
  • 实现了CommandLineRunner的类可以在Spring Boot应用程序启动后执行代码,通常用于执行一些启动后的任务,或者在命令行界面中与用户交互。
顺序 注解 描述
1 @PostConstruct 在依赖注入完成后,初始化前后执行。这是一个JSR-250注解。
2 @Autowired 自动装配Spring容器中的bean。它允许你将Spring bean注入到当前类中。
3 @PreDestroy 在bean销毁之前执行。这也是一个JSR-250注解。
4 ApplicationListener 实现ApplicationListener接口的类可以监听特定类型的事件。
5 @ApplicationRunner 用于在Spring应用程序启动后执行代码。
6 CommandLineRunner 类似于ApplicationRunner,但用于在命令行应用程序启动后执行代码。

相关推荐

  1. SpringBoot启动流程

    2024-07-20 07:12:06       17 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 07:12:06       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 07:12:06       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 07:12:06       45 阅读
  4. Python语言-面向对象

    2024-07-20 07:12:06       55 阅读

热门阅读

  1. mybatis-sql实战总结

    2024-07-20 07:12:06       17 阅读
  2. Python--正则表达式re模块基础匹配方法

    2024-07-20 07:12:06       16 阅读
  3. 2024-07-19 Unity插件 Odin Serializer1 —— 插件介绍

    2024-07-20 07:12:06       17 阅读
  4. 【多商户自营解决方案】

    2024-07-20 07:12:06       16 阅读
  5. 基于深度学习的股票预测

    2024-07-20 07:12:06       15 阅读
  6. centos(或openEuler系统)安装clickhouse集群

    2024-07-20 07:12:06       14 阅读
  7. 介绍下项目的架构

    2024-07-20 07:12:06       11 阅读
  8. Docker 和 k8s 之间是什么关系?

    2024-07-20 07:12:06       14 阅读
  9. 软考高级第四版备考--第25天干系人绩效

    2024-07-20 07:12:06       16 阅读