Spring事件驱动

        本文主要介绍在Spring环境中,如何使用事件机制。

 一、快速上手

        首先借助org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent)来发布事件,而接收方,则在直接处理的方法上,添加@EventListener注解即可。

1. 事件定义

发布一个事件,所以第一件事要做的就是定义一个事件,对于Spring而言,要求自定义的事件继承一个ApplicationEvent类,一个简单的demo如下:

public class NotifyEvent extends ApplicationEvent {
    @Getter
    private String msg;

    public NotifyEvent(Object source, String msg) {
        super(source);
        this.msg = msg;
    }

}

2. 发布事件

发布事件则比较简单,直接拿到ApplicationContext实例,执行publish方法即可,如下给出一个简单的发布类:

@Component
public class NotifyPublisher implements ApplicationContextAware {

    private ApplicationContext apc;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.apc = applicationContext;
    }

    public void publishEvent(String msg){
        apc.publishEvent(new NotifyEvent(this, msg)); 
    }
}

3. 事件监听器

在方法上添加注释即可,如下

@Component
public class NotifyQueueListener {

    @EventListener
    public void consumerA(NotifyEvent notifyEvent){

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("A: "  + Thread.currentThread().getName()+ "|" + notifyEvent.getMsg());
    }

    

   
}

二、疑问与解答

1.发布与监听器的关联

        上面给出了快速入门,看起来并不复杂,也比较容易使用,但是有一个问题需要在使用之前弄明白了,发布事件和监听器是怎样关联起来的呢?

那么如果发布者,推送的是一个NotifyEvent类型的事件,那么接收者是怎样的呢?

  • 根据方法参数类型执行
  • 参数为NotifyEvent以及其子类的监听器,都可以接收到消息

测试用例如下:

NewNotifyEvent 继承自上面的NotifyEvent

public class NewNotifyEvent extends NotifyEvent {
    @Getter
    private int version;

    public NewNotifyEvent(Object source, String msg) {
        super(source, msg);
    }
    public NewNotifyEvent(Object source, String msg, int version) {
        super(source, msg);
        this.version = version;
    }
}

发布事件修改如下:

@Component
public class NotifyPublisher implements ApplicationContextAware {

    private ApplicationContext apc;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.apc = applicationContext;
    }

    public void publishEvent(int status, String msg){
        if (status == 0){
            apc.publishEvent(new NotifyEvent(this, msg));
        }else {
            apc.publishEvent(new NewNotifyEvent(this, msg , (int) (System.currentTimeMillis()/1000)));
        }

    }
}

 事件监听修改如下:

@Component
public class NotifyQueueListener {

    @EventListener
    public void consumerA(NotifyEvent notifyEvent){

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("A: "  + Thread.currentThread().getName()+ "|" + notifyEvent.getMsg());
    }

    @EventListener
    public void consumerB(NewNotifyEvent newNotifyEvent){

        System.out.println("B: "  + Thread.currentThread().getName()+ "|" + newNotifyEvent.getMsg());
    }

    @EventListener
    public void consumerC(NotifyEvent notifyEvent){

        System.out.println("C: "  + Thread.currentThread().getName()+ "|" + notifyEvent.getMsg());
    }

   
}

测试代码

@RunWith(SpringRunner.class)
@SpringBootTest
@EnableAutoConfiguration
public class EventTest {

    @Autowired
    private NotifyPublisher notifyPublisher;
    @Test
    public void testPublishEvent(){
        notifyPublisher.publishEvent(1,"新的发布事件! NewNotify");
        System.out.println("---------");
        notifyPublisher.publishEvent(0,"旧的发布事件! Notify");
    }

}

输出结果如下,对于NewNotifyEvent, 参数类型为NotifyEvent的consumerA, consumerC都可以接收到

A: main|新的发布事件! NewNotify
C: main|新的发布事件! NewNotify
B: main|新的发布事件! NewNotify
---------
A: main|旧的发布事件! Notify
C: main|旧的发布事件! Notify

2. 消息接收的顺序

上面消息处理是串行的,那么先后顺序怎么确定? (下面的答案不确定,有待深入源码验证!!!)

  • 先扫描到的bean先处理
  • 同一个bean中,按精确匹配,先后定义顺序进行

3. 异步消费

对于异步消费,即在消费者方法上添加一个@Async注解,并需要在配置文件中,开启异步支持

@Async
@EventListener
public void processNewNotifyEvent(NewNotifyEvent newNotifyEvent) {
    System.out.println("new notifyevent: " + newNotifyEvent.getMsg() + " : " + newNotifyEvent.getVersion());
}

配置支持

@Configuration
@EnableAsync
public class AysncListenerConfig implements AsyncConfigurer {
    /**
     * 获取异步线程池执行对象
     * @return
     */
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolExecutor(5,5,1, TimeUnit.MINUTES, new LinkedBlockingQueue());
    }
    }
}

执行结果如下:

A: main|新的发布事件! NewNotify
C: main|新的发布事件! NewNotify
B: main|新的发布事件! NewNotify
pool-1-thread-1 new notifyevent: 新的发布事件! NewNotify : 1688383517
---------
A: main|旧的发布事件! Notify
C: main|旧的发布事件! Notify

参考传送门:

180609-Spring之事件驱动机制的简单使用 - 一灰灰Blog

你可能感兴趣的:(spring,java,后端)