优化你的Spring Boot应用:预加载的秘密

简介: 优化你的Spring Boot应用:预加载的秘密


预加载

当我们出现一些需求需要在项目启动的时候就去运行某个方法,比如定时任务

ApplicationListener实现

ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。

如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。

其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener接口可以收到监听动作,然后可以写自己的逻辑

内置事件

  • ContextRefreshedEvent

ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用

  • ContextStartedEvent

当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。

  • ContextStoppedEvent

当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。

  • ContextClosedEvent

当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。

  • RequestHandledEvent

这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

实现

比如要监听ContextRefreshedEvent的时可以实现ApplicationListener接口,并且传入要监听的事件

/**
 * @author xiaobo
 * @date 2022/3/29
 */
@Component
@Slf4j
public class SysQuartzJobListener implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private ISysQuartzJobService sysQuartzJobService;
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        try {
            sysQuartzJobService.updateJobStartById("23dedbb5-af24-11ec-a42d-0894ef72d9c4");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        System.out.println(contextRefreshedEvent);
        System.out.println("listen..........");
    }
}

自定义实例

可以自定义事件,然后做完业务处理后手动发出。同上集成某个监听接口,接收到事件后进行业务处理

事件定义:

public class EmailEvent extends ApplicationEvent{
   private String address;
   private String text;
   public EmailEvent(Object source, String address, String text){
   super(source);
      this.address = address;
      this.text = text;
   }
   public EmailEvent(Object source) {
     super(source);
   }
   //......address和text的setter、getter
}

监听定义

public class EmailNotifier implements ApplicationListener{
   public void onApplicationEvent(ApplicationEvent event) {
     if (event instanceof EmailEvent) {
        EmailEvent emailEvent = (EmailEvent)event;
        System.out.println("邮件地址:" + emailEvent.getAddress());
        System.our.println("邮件内容:" + emailEvent.getText());
     } else {
        System.our.println("容器本身事件:" + event);
     }
   }
}

业务触发

public class SpringTest {
   public static void main(String args[]){
     ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
     //创建一个ApplicationEvent对象
     EmailEvent event = new EmailEvent("hello","abc@163.com","This is a test");
     //主动触发该事件
     context.publishEvent(event);
   }
}

**注意⚠️:**不管是内置监听还是外部自定义监听一定要把实现ApplicationListener的类定义成一个bean才行,可以是通过注解@Component等也可以通过xml的方式去执行。

SpringBoot的CommandLineRunner接口

当容器初始化完成之后会调用CommandLineRunner中的run()方法,同样能够达到容器启动之后完成一些事情。这种方式和ApplicationListener相比更加灵活,如下:

  • 不同的CommandLineRunner实现可以通过@Order()指定执行顺序
  • 可以接收从控制台输入的参数。
/**
 * @author xiaobo
 * @date 2022/3/29
 */
@Component
@Slf4j
public class SysQuartzJobRunner implements CommandLineRunner {
    @Autowired
    private ISysQuartzJobService sysQuartzJobService;
    @Override
    public void run(String... args) throws Exception {
        try {
            sysQuartzJobService.updateJobStartById("23dedbb5-af24-11ec-a42d-0894ef72d9c4");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        log.info("test");
    }
}

这里的String… 可以换为ApplicationArguments

当bean加载完初始化的时候,调用

这里先讲以下这个注解PostConstruct

首先这个注解是java提供的,为不是Spring提供的

该注解的方法在整个bean的 初始化中的执行顺序是:

@PostConstruct 注解的方法在整个 Bean 的初始化过程中的执行顺序如下:

  1. Bean的实例化(Instantiation): 首先,Spring会实例化一个 Bean。这通常涉及到调用 Bean 类的构造函数来创建对象。在这一步完成后,Bean 的属性尚未设置。
  2. 依赖注入(Dependency Injection): 如果 Bean 有依赖关系,例如其他 Bean 或配置属性,Spring 将会注入这些依赖。这是通过自动装配(Autowired)或显式配置(例如通过 XML 或 Java 配置类)完成的。
  3. @PostConstruct 方法的调用: 一旦依赖注入完成,Spring 会查找使用了 @PostConstruct 注解的方法,并在此时调用它们。这些方法用于执行 Bean 的初始化操作。可以有多个方法都使用了 @PostConstruct 注解,它们的执行顺序取决于它们在类中的声明顺序。
  4. Bean 初始化完成: 一旦 @PostConstruct 方法执行完毕,Bean 就被认为是初始化完成的,可以正常使用了。此后,Bean 就可以响应应用程序的请求并执行其它业务逻辑。

需要注意的是,@PostConstruct 注解标记的方法只会在 Bean 的初始化阶段执行一次。如果你有多个 Bean,它们都使用了 @PostConstruct 注解,Spring 会按照它们在各自类中的声明顺序执行这些方法。

总之,@PostConstruct 注解方法提供了一个在 Bean 初始化完成后执行自定义初始化逻辑的方式,确保 Bean 处于可用状态。

注意⚠️:子类实例话过程中会调用父类的加了该注解的方法

具体实现如下:

@Component
@Slf4j
public class SysQuartzJobTest {
    @Autowired
    private ISysQuartzJobService sysQuartzJobService;
    @PostConstruct
    public void test() throws Exception{
        try {
            sysQuartzJobService.updateJobStartById("23dedbb5-af24-11ec-a42d-0894ef72d9c4");
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        log.info("PostConstruct测试");
    }
}

异步任务

  1. 创建一个异步方法:
    首先,你需要在Spring Bean中创建一个异步方法,用于执行预加载操作。你可以在这个方法上添加@Async注解,以便告诉Spring该方法应该在异步线程中执行。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class MyService {
    @Async
    public void preloadData() {
        // 执行预加载操作的代码
    }
}
  1. 配置TaskExecutor
    在你的应用中配置一个TaskExecutor,这将用于执行异步方法。你可以使用Spring Boot提供的默认TaskExecutor,或者自定义一个,具体取决于你的需求。
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
@Component
public class TaskExecutorConfig {
    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
}
  1. 调用异步方法:
    在你的应用中调用异步方法,这将触发预加载操作。由于该方法被标记为异步,调用它不会阻塞主线程。
@Autowired
private MyService myService;
public void someMethod() {
    // 其他同步操作
    myService.preloadData(); // 调用异步方法
    // 继续其他同步操作
}
  1. 触发预加载操作:
    当你调用异步方法时,Spring Boot会自动将该方法委托给TaskExecutor,并在一个独立的线程中执行。这样,你的预加载操作将在后台运行,不会阻塞主线程,从而提高了应用的性能和响应性。

使用异步任务来实现预加载特别适合那些需要执行耗时操作的场景,如初始化大量数据、加载远程资源或执行复杂的计算。通过将这些操作放入异步方法中,你可以确保应用仍能保持高度响应,并且不会因为预加载而导致阻塞。

相关文章
|
1月前
|
监控 Java 数据处理
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
【Spring云原生】Spring Batch:海量数据高并发任务处理!数据处理纵享新丝滑!事务管理机制+并行处理+实例应用讲解
|
1月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
29天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
66 1
|
2天前
|
缓存 NoSQL Java
Springboot 大事务问题的常用优化方案
Springboot 大事务问题的常用优化方案
|
24天前
|
存储 安全 Java
Spring Security应用讲解(Java案列演示)
Spring Security应用讲解(Java案列演示)
|
29天前
|
Prometheus 监控 Cloud Native
Spring Boot 应用可视化监控
Spring Boot 应用可视化监控
18 0
|
1月前
|
Cloud Native Java 开发者
Spring Boot 4.0:构建云原生Java应用的前沿工具
Spring Boot 4.0:构建云原生Java应用的前沿工具
|
1月前
|
存储 NoSQL Java
【十】springboot整合quartz实现定时任务优化
【十】springboot整合quartz实现定时任务优化
31 0
|
1月前
|
安全 Java 数据安全/隐私保护
【Java】保护你的应用:深入探讨Spring Security的应用与最佳实践
【Java】保护你的应用:深入探讨Spring Security的应用与最佳实践
76 0
|
1月前
|
Java 大数据 数据库
【Spring底层原理高级进阶】Spring Batch清洗和转换数据,一键处理繁杂数据!Spring Batch是如何实现IO流优化的?本文详解!
【Spring底层原理高级进阶】Spring Batch清洗和转换数据,一键处理繁杂数据!Spring Batch是如何实现IO流优化的?本文详解!