SpringBoot启动时运行特定代码的多种方式小结

 更新时间:2025年01月26日 10:35:01   作者:一叶飘零_sweeeet  
SpringBoot提供了多种方式在应用程序启动时运行特定的代码,包括CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件机制和自定义注解等,下面就来具体介绍一下

一、使用 CommandLineRunner 和 ApplicationRunner 接口

(一)CommandLineRunner 和 ApplicationRunner 介绍

  • 功能概述
    • CommandLineRunner 和 ApplicationRunner 是 Spring Boot 提供的两个接口,用于在应用程序启动后运行一些特定的代码。它们可以接收应用程序启动时传入的命令行参数,并执行相应的操作。
  • 区别与联系
    • 两者的主要区别在于接收参数的方式不同。CommandLineRunner 接收的是字符串数组形式的命令行参数,而 ApplicationRunner 接收的是 ApplicationArguments 对象,该对象提供了更丰富的方法来处理命令行参数。

(二)使用 CommandLineRunner

  • 实现 CommandLineRunner 接口
    • 创建一个类并实现 CommandLineRunner 接口,然后在 run 方法中编写要在启动时执行的代码。
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

    @Override
    public void run(String... args) {
        System.out.println("Application started with command line arguments: " + java.util.Arrays.toString(args));
    }
}
  • 命令行参数传递
    • 在启动应用程序时,可以通过命令行传递参数。这些参数将被传递给 CommandLineRunner 的 run 方法。
    • 例如,使用以下命令启动应用程序:java -jar myapp.jar arg1 arg2。在 run 方法中,args 参数将包含 ["arg1", "arg2"]。

(三)使用 ApplicationRunner

  • 实现 ApplicationRunner 接口
    • 与 CommandLineRunner 类似,创建一个类并实现 ApplicationRunner 接口,在 run 方法中编写启动时要执行的代码。
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        System.out.println("Application started with application arguments: " + args.getNonOptionArgs());
    }
}
  • 处理 ApplicationArguments
    • ApplicationArguments 对象提供了一些方法来处理命令行参数。例如,可以使用 getNonOptionArgs 方法获取非选项参数,使用 getOptionValues 方法获取选项参数的值。

二、使用 @PostConstruct 注解

(一)@PostConstruct 注解介绍

  • 功能说明
    • @PostConstruct 注解是 Java EE 中的一个注解,用于标注一个方法,该方法将在 bean 的初始化完成后被调用。在 Spring Boot 中,可以使用这个注解来在应用程序启动时执行一些特定的代码。
  • 执行顺序
    • @PostConstruct 注解标注的方法将在 bean 的构造函数和依赖注入完成后被调用。如果一个类中有多个方法被标注为 @PostConstruct,它们将按照方法声明的顺序依次执行。

(二)使用示例

  • 在 bean 中使用 @PostConstruct
    • 创建一个 bean 类,并在其中的一个方法上使用 @PostConstruct 注解。
import javax.annotation.PostConstruct;

import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @PostConstruct
    public void init() {
        System.out.println("MyBean initialized.");
    }
}
  • 多个 bean 中的 @PostConstruct 方法执行顺序
    • 如果在多个 bean 中都使用了 @PostConstruct 注解,它们的执行顺序取决于 bean 的创建顺序和依赖关系。一般来说,先创建的 bean 中的 @PostConstruct 方法会先执行。

三、使用 InitializingBean 接口

(一)InitializingBean 接口介绍

  • 功能概述
    • InitializingBean 是 Spring 框架中的一个接口,实现该接口的 bean 在所有属性被设置后会调用 afterPropertiesSet 方法。这个方法可以用于在 bean 初始化完成后执行一些特定的代码。
  • 与 @PostConstruct 注解的比较
    • 与 @PostConstruct 注解类似,InitializingBean 接口也用于在 bean 初始化完成后执行代码。但是,使用 InitializingBean 接口需要实现一个特定的方法,而 @PostConstruct 注解只需要标注一个方法即可。

(二)使用示例

  • 实现 InitializingBean 接口
    • 创建一个类并实现 InitializingBean 接口,然后在 afterPropertiesSet 方法中编写要在启动时执行的代码。
import org.springframework.beans.factory.InitializingBean;

import org.springframework.stereotype.Component;

@Component
public class MyInitializingBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MyInitializingBean initialized.");
    }
}
  • 处理异常
    • afterPropertiesSet 方法可能会抛出异常。如果在这个方法中发生了异常,Spring 容器会将异常记录下来,并继续处理其他 bean 的初始化。因此,在实现 afterPropertiesSet 方法时,应该处理可能发生的异常,以确保应用程序的正常启动。

四、使用 Spring Boot 的事件机制

(一)Spring Boot 事件机制介绍

  • 事件驱动架构
    • Spring Boot 提供了一个事件驱动的架构,允许开发者在应用程序的不同阶段发布和监听事件。通过使用事件机制,可以在应用程序启动、关闭等阶段执行特定的代码。
  • 事件类型
    • Spring Boot 中有多种事件类型,例如 ApplicationStartedEvent、ApplicationReadyEvent、ApplicationFailedEvent 等。这些事件分别在应用程序启动开始、启动完成、启动失败等阶段被发布。

(二)使用示例

  • 定义事件监听器
    • 创建一个类并实现 ApplicationListener 接口,指定要监听的事件类型。在 onApplicationEvent 方法中编写要在事件发生时执行的代码。
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationStartedEvent;

import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationStartedEvent> {

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        System.out.println("Application started.");
    }
}
  • 发布事件
    • 在某些情况下,可能需要手动发布事件。可以使用 ApplicationEventPublisher 接口来发布事件。
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

@Component
public class MyEventPublisher implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void publishEvent() {
        MyCustomEvent event = new MyCustomEvent(this);
        applicationEventPublisher.publishEvent(event);
    }
}

五、使用自定义注解和 AOP

(一)自定义注解介绍

  • 定义自定义注解
    • 可以定义一个自定义注解,用于标注需要在应用程序启动时执行的方法。然后,使用 AOP(面向切面编程)来拦截被标注的方法,并在应用程序启动时执行它们。
  • 注解属性
    • 自定义注解可以包含一些属性,用于指定方法的执行顺序、条件等。例如,可以定义一个 @RunAtStartup 注解,用于标注需要在应用程序启动时执行的方法,并包含一个 order 属性,用于指定方法的执行顺序。

(二)使用示例

  • 定义自定义注解
    • 创建一个自定义注解,例如 @RunAtStartup。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunAtStartup {
    int order() default 0;
}
  • 使用 AOP 拦截被标注的方法
    • 创建一个切面类,使用 @Aspect 注解标注,并在其中定义一个方法,用于拦截被 @RunAtStartup 注解标注的方法。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class StartupAspect {

    @Around("@annotation(com.example.RunAtStartup)")
    public Object runAtStartup(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Running method annotated with @RunAtStartup.");
        return joinPoint.proceed();
    }
}
  • 在方法上使用自定义注解
    • 在需要在应用程序启动时执行的方法上使用 @RunAtStartup 注解。
import org.springframework.stereotype.Component;

@Component
public class MyComponent {

    @RunAtStartup(order = 1)
    public void startupMethod1() {
        System.out.println("Startup method 1 executed.");
    }

    @RunAtStartup(order = 2)
    public void startupMethod2() {
        System.out.println("Startup method 2 executed.");
    }
}

六、实际案例分析

(一)案例背景

假设有一个企业级应用程序,需要在启动时进行一些初始化操作,例如加载配置文件、连接数据库、初始化缓存等。

(二)技术选型

  • 使用 Spring Boot 框架
    • 选择 Spring Boot 作为开发框架,因为它提供了丰富的功能和便捷的开发方式。Spring Boot 的自动配置和起步依赖使得应用程序的开发更加高效。
  • 结合多种启动时执行代码的方式
    • 根据具体的需求,结合使用 CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件机制和自定义注解等方式,在应用程序启动时执行不同的初始化操作。

(三)具体实现

  • 使用 CommandLineRunner 和 ApplicationRunner 进行参数处理
    • 创建一个 CommandLineRunner 或 ApplicationRunner 的实现类,用于处理应用程序启动时传入的命令行参数。例如,可以根据命令行参数来选择不同的配置文件或数据库连接。
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) {
        if (args.containsOption("config")) {
            String configFile = args.getOptionValues("config").get(0);
            // 加载指定的配置文件
        }
    }
}
  • 使用 @PostConstruct 和 InitializingBean 进行 bean 初始化
    • 在一些 bean 中使用 @PostConstruct 注解或实现 InitializingBean 接口,在 bean 初始化完成后执行一些特定的代码。例如,可以在连接数据库的 bean 中使用 @PostConstruct 注解来在 bean 初始化完成后建立数据库连接。
import javax.annotation.PostConstruct;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class DatabaseConnectionBean implements InitializingBean {

    @PostConstruct
    public void init() {
        System.out.println("Preparing to connect to database.");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 建立数据库连接
        System.out.println("Connected to database.");
    }
}
  • 使用事件机制进行启动通知
    • 创建一个事件监听器,用于在应用程序启动完成后发送通知。例如,可以在应用程序启动完成后发送一封电子邮件通知管理员。
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    private final JavaMailSender javaMailSender;

    public MyApplicationListener(JavaMailSender javaMailSender) {
        this.javaMailSender = javaMailSender;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo("admin@example.com");
        message.setSubject("Application started");
        message.setText("The application has started successfully.");
        javaMailSender.send(message);
    }
}
  • 使用自定义注解进行特定方法的执行
    • 定义一个自定义注解,并使用 AOP 拦截被标注的方法。例如,可以定义一个 @InitializeCache 注解,用于标注需要在应用程序启动时初始化缓存的方法。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InitializeCache {
}
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class CacheInitializationAspect {

    @Around("@annotation(com.example.InitializeCache)")
    public Object initializeCache(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Initializing cache.");
        return joinPoint.proceed();
    }
}
import org.springframework.stereotype.Component;

@Component
public class CacheManager {

    @InitializeCache
    public void initCache() {
        // 初始化缓存
        System.out.println("Cache initialized.");
    }
}

(四)效果评估

  • 初始化操作的可靠性
    • 通过在应用程序启动时执行这些初始化操作,可以确保应用程序在运行之前已经完成了必要的准备工作。这提高了应用程序的可靠性和稳定性。
  • 灵活性和可扩展性
    • 使用多种方式来执行启动时的代码,使得应用程序具有更高的灵活性和可扩展性。可以根据具体的需求选择不同的方式来执行初始化操作,并且可以方便地添加新的初始化操作。
  • 代码的可读性和可维护性
    • 将启动时的代码分离到不同的类和方法中,使用合适的命名和注释,可以提高代码的可读性和可维护性。同时,使用 Spring Boot 的自动配置和注解,使得代码更加简洁和易于理解。

七、性能考虑和最佳实践

(一)性能影响

  • 启动时间
    • 在应用程序启动时执行大量的代码可能会增加启动时间。因此,应该尽量减少启动时的代码量,只执行必要的初始化操作。
  • 资源消耗
    • 一些初始化操作可能会消耗大量的资源,例如连接数据库、加载大型配置文件等。应该合理安排这些操作,避免在启动时过度消耗资源,影响应用程序的性能。

(二)最佳实践

  • 分离初始化操作
    • 将不同的初始化操作分离到不同的类和方法中,以便更好地管理和维护。可以根据功能模块或业务需求进行分类,使得代码更加清晰和易于理解。
  • 延迟初始化
    • 对于一些不是立即需要的初始化操作,可以考虑延迟初始化。例如,可以在第一次使用某个功能模块时才进行初始化,而不是在应用程序启动时就进行初始化。
  • 使用异步初始化
    • 如果某些初始化操作比较耗时,可以考虑使用异步方式进行初始化。这样可以避免阻塞应用程序的启动过程,提高应用程序的响应速度。
  • 监控和日志记录
    • 在执行启动时的代码时,应该进行监控和日志记录,以便及时发现和解决问题。可以使用 Spring Boot 的日志框架来记录初始化操作的进度和结果,以便在出现问题时进行排查。

八、常见问题及解决方案

(一)多个启动时执行的代码之间的依赖关系问题

  • 问题描述
    • 如果多个启动时执行的代码之间存在依赖关系,可能会导致代码执行顺序不正确或出现错误。例如,如果一个方法需要在另一个方法执行完成后才能执行,但是由于启动时代码的执行顺序不确定,可能会导致依赖关系无法满足。
  • 解决方案
    • 可以使用 @DependsOn 注解来指定 bean 之间的依赖关系,确保依赖的 bean 先被创建和初始化。另外,可以使用事件机制来协调不同的初始化操作,确保它们按照正确的顺序执行。

(二)启动时代码执行失败的处理

  • 问题描述
    • 如果启动时执行的代码出现错误,可能会导致应用程序无法正常启动。例如,如果连接数据库失败,可能会导致应用程序无法继续启动。
  • 解决方案
    • 应该在启动时的代码中进行适当的错误处理,捕获可能出现的异常,并进行相应的处理。可以记录错误日志,发送通知给管理员,或者采取其他措施来确保应用程序能够继续启动或提供适当的错误信息给用户。

九、总结与展望

Spring Boot 提供了多种方式来在应用程序启动时运行特定的代码。通过使用 CommandLineRunner、ApplicationRunner、@PostConstruct、InitializingBean、事件机制和自定义注解等方式,可以在应用程序启动时进行各种初始化操作,提高应用程序的可靠性和稳定性。在实际应用中,应该根据具体的需求选择合适的方式,并注意性能和可维护性的考虑。

到此这篇关于SpringBoot启动时运行特定代码的多种方式小结的文章就介绍到这了,更多相关SpringBoot启动时运行特定代码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 如何使用Guava Cache做缓存

    如何使用Guava Cache做缓存

    Cache在ConcurrentHashMap的基础上提供了自动加载数据、清除数据、get-if-absend-compute的功能,本文给大家介绍如何使用Guava Cache做缓存,感兴趣的朋友一起看看吧
    2023-11-11
  • Java方法重载的使用实战案例

    Java方法重载的使用实战案例

    这篇文章主要介绍了Java方法重载的使用,结合具体实例形式分析了java方法重载的基本使用技巧与相关操作注意事项,需要的朋友可以参考下
    2019-09-09
  • 如何利用Stream改变list中特定对象的某一属性

    如何利用Stream改变list中特定对象的某一属性

    这篇文章主要介绍了如何利用Stream改变list中特定对象的某一属性问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Java线程池execute()方法源码全面解析

    Java线程池execute()方法源码全面解析

    这篇文章主要介绍了Java线程池execute()方法源码全面解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • MyEclipse配置JDK的全过程

    MyEclipse配置JDK的全过程

    这篇文章主要介绍了MyEclipse配置JDK的全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • 关于maven全局配置文件settings.xml解析

    关于maven全局配置文件settings.xml解析

    这篇文章主要介绍了关于maven全局配置文件settings.xml,具有很好的参考价值,希望对大家有所帮助。
    2022-03-03
  • JAVAlogback日志管理详解

    JAVAlogback日志管理详解

    本篇文章主要介绍了在SpringBoot中使用Logback管理记录日志,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-09-09
  • java集合模拟实现斗地主洗牌和发牌

    java集合模拟实现斗地主洗牌和发牌

    这篇文章主要为大家详细介绍了java集合模拟实现斗地主洗牌和发牌,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • SpringBoot实现动态控制定时任务支持多参数功能

    SpringBoot实现动态控制定时任务支持多参数功能

    这篇文章主要介绍了SpringBoot实现动态控制定时任务-支持多参数功能,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-05-05
  • Java 实现二叉搜索树的查找、插入、删除、遍历

    Java 实现二叉搜索树的查找、插入、删除、遍历

    本文主要介绍了Java实现二叉搜索树的查找、插入、删除、遍历等内容。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02

最新评论