CommandLineRunner 运行() 方法和 RabbitMQ listener() 方法之间的执行顺序

Order of execution between CommandLineRunner run() method and RabbitMQ listener() method

我的 Spring 引导应用程序正在通过 RabbitMQ 订阅一个事件。 另一个 Web 应用程序负责将事件发布到我的应用程序正在侦听的队列中。 该事件基本上包含学院信息。 主应用程序 class 实现 CommandLineRunner 并覆盖 运行() 方法。 此 运行() 方法调用创建管理员用户的方法。

当我的应用程序启动时,当事件已存在于队列中时,我的应用程序中的侦听器应该更新管理员用户的学院 ID。 然而,看起来 createAdmin() 和 listener() 正在并行执行,并且研究所 ID 永远不会更新。帮助我理解控制流程。

查看下面的代码片段和打印语句的顺序。

@SpringBootApplication
public class UserManagementApplication implements CommandLineRunner{

    public static void main(String[] args)  {
        SpringApplication.run(UserManagementApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        
        createAdmin();
    }
    
    private void createAdmin() {
        System.out.println("************** createAdmin invoked *********************");
        Optional<AppUserEntity> user = appUserService.getUserByUserName("superuser");
        
        if(!user.isPresent()) {
            AppUserEntity superuser = new AppUserEntity();
            superuser.setUsername("superuser");
            superuser.setAppUserRole(AppUserRole.SUPERADMIN);
            
            superuser.setInstId(null); // will be set when Queue receives Institute information 
            
            appUserService.saveUser(superuser);
            System.out.println("************** superuser creation SUCCESSFUL *********************");
        }
    }
}

@Component
public class InstituteQueueListener {

    @RabbitListener(queues = "institute-queue")
    public void updateSuperAdminInstituteId(InstituteEntity institute) {
        
        System.out.println("************** RabbitListener invoked *********************");
        
        Long headInstituteId = institute.getInstId();
        
        Optional<AppUserEntity> user = appUserService.getUserByUserName("superuser");
        if(user.isPresent()) {
            System.out.println("************* superuser is present *****************");
            AppUserEntity superuser = user.get();
            superuser.setInstId(headInstituteId);
            System.out.println("************* Going to save inst Id = "+headInstituteId);
            appUserService.saveUser(superuser);
        }
        
        System.out.println("************** superuser is NOT present (inside Q listener)*********************");
    }

}

Order of print statements ....
(the queue already has event before running my application)
System.out.println("************** createAdmin invoked *********************");
System.out.println("************** RabbitListener invoked *********************");
System.out.println("************** superuser is NOT present (inside Q listener) *********************");
System.out.println("************** superuser creation SUCCESSFUL *********************");

当您启动您的应用程序时,任何 CommandLineRunner 都会在主线程(您调用 SpringApplication.run 的线程)上调用。一旦应用程序上下文被刷新并且它的所有 bean 都被初始化,就会发生这种情况。

@RabbitListener - 消息侦听器容器一旦启动容器并在消息可用时调用带注释的方法。容器作为正在刷新的应用程序上下文的一部分启动,因此在调用命令行运行程序之前启动。容器使用单独的线程池来调用其侦听器。

这意味着您的侦听器方法可能会在命令行运行程序之前、同时或之后被调用,具体取决于队列中是否有消息(事件)。