优化你的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,并在一个独立的线程中执行。这样,你的预加载操作将在后台运行,不会阻塞主线程,从而提高了应用的性能和响应性。

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

相关文章
|
18天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
42 14
|
16天前
|
XML 前端开发 安全
Spring MVC:深入理解与应用实践
Spring MVC是Spring框架提供的一个用于构建Web应用程序的Model-View-Controller(MVC)实现。它通过分离业务逻辑、数据、显示来组织代码,使得Web应用程序的开发变得更加简洁和高效。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring MVC,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
45 2
|
25天前
|
JSON 安全 算法
Spring Boot 应用如何实现 JWT 认证?
Spring Boot 应用如何实现 JWT 认证?
55 8
|
1月前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
23天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
36 1
|
1月前
|
缓存 监控 Java
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
80 2
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
47 0
|
4月前
|
运维 Java Nacos
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
|
6月前
|
Java 应用服务中间件 Maven
ContextLoaderListener在Spring应用中的作用与配置方法
ContextLoaderListener在Spring应用中的作用与配置方法
下一篇
DataWorks