阅读 1831

Spring框架中的事件订阅发布

这是我参与更文挑战的第18天,活动详情查看: 更文挑战

首先说一下我为什么使用事件,比如现在创建一个订单但是我创建成功后要给客户发送一条短信和一个邮件提醒,本身没创建订单一系列操作就需要很多时间但是我还要去发送短信和邮件,期间还要调用其它服务来实现耗时比较长达不到客户的满意度,所以使用的方式可以说一下:

  1. activeMQ(异步)
  2. 使用spring事件监听(同步+异步)

Spring支持的事件监听

什么是ApplicationContext

它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些。ApplicationContext则是应用的容器。Spring把Bean(object)放在容器中,需要用就通过get方法取出来。此接口提供给Spring应用配置的能力,当应用启动时,此接口的实现是只读的,但是如果该实现支持,其内容也是可以重新载入的。 我们总结一下: ApplicationContext大概提功能如下能力:

  • 获取应用组件的bean工厂方法,此能力继承org.springframework.beans.factory.ListableBeanFactory
  • 加载资源文件的能力,此能力继承自org.springframework.core.io.ResourceLoader
  • 发布事件到已注册的监听器,此能力继承自ApplicationEventPublisher
  • 提供国际化的消息访问,此能力继承自MessageSource

好对ApplicationContext有一定了解之后我们再来看看Spring提供的事件监听。为了实现事件监听的能力Spring为我们提供了两个顶层接口/抽象类,ApplcationEvent:是个抽象类,里面只有一个构造函数和一个长整型的timestamp。我们自定义的Application event 需要继承这个抽象类.ApplicationListener:是一个接口,里面只有一个方法onApplicationEvent ,每一个实现改接口的类都要自己实现这个方法。Spring的事件监听是基于标准的观察者模式的,如果在ApplicationContext部署了一个实现了ApplicationListener的bean,那么当一个ApplicationEvent发布到ApplicationContext时,这个bean得到通知并作特定的处理。从上面这段话我们很容易产生两点思考:

  • 实现了ApplicationListener的bean如何部署到ApplicationContext
  • 一个ApplicationEvent如何发布到ApplicationContext

所以一个完整的事件,由事件源、事件发布、事件监听三部分组成

代码示例

声明事件

public class SendEmailEvent extends ApplicationEvent {
    private String message;

    public SendEmailEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String sendEmail(){
        return message;
    }

}
复制代码

事件发布

@Component
@Slf4j
public class SendEmailEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publish(final String message) {
        log.info("publis a SendEmailEvent,message:{}", message + " time: " + LocalTime.now());
        SendEmailEvent sendEmailEvent = new SendEmailEvent(this, message);
        applicationEventPublisher.publishEvent(sendEmailEvent);
    }

}

复制代码

事件监听

事件监听方式一
@Component
@Slf4j
public class SendEmailListener implements ApplicationListener<SendEmailEvent> {
    @Override
    public void onApplicationEvent(SendEmailEvent sendEmailEvent) {
        String s = sendEmailEvent.sendEmail();
        log.info("SendRegisterEmailListener message: " + s+" time: "+ LocalTime.now());
    }
}
复制代码
事件监听实现方式二
@Component
@Slf4j
public class SendEmailListener2 {
    @EventListener
    public void sendEmmail(SendEmailEvent sendEmailEvent){
        String s = sendEmailEvent.sendEmail();
        log.info("SendRegisterEmailListener message: " + s+" time: "+ LocalTime.now());
    }

}
复制代码

测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class SendEmailEventPublisherTest {
    @Resource
    private SendEmailEventPublisher sendEmailEventPublisher;

    @Test
    public void publish() {
        sendEmailEventPublisher.publish("啦啦啦");
    }
}

复制代码

Spring事件默认是同步的,通过在启动类Application上加上@EnableAsync开启异步。使用方法注解@Async可以作用在监听器的执行方法上,异步执行。@Async不带参数默认使用SpringBoot默认的线程池。推荐使用自定义的线程池:

@Configuration
public class ThreadPoolConfig {

    @Bean("executor")
    public Executor getExecutor() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10,
                20,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(10000));
        return executor;
    }
}
复制代码

总结

  • 通过事件机制、可以让代码解耦,让自己的代码,非常的干净,拓展性更强;
  • 异步的时间处理机制,可以提高程序的响应速度,且内部的线程池会大大提高程序的并发效率
  • 条件化和泛型化的监听器可以让你减少很多显式的逻辑判断,从而让每个时间监听的原子性更强
文章分类
后端
文章标签