目录
前言
发布与订阅是一种典型的生产者与消费者模型,它可以让服务之间的调用变的更加灵活易扩展,数据共享更加方便,高内聚低耦合,思想很重要,值得学习。
1、事件-监听模型
Spring是基于
事件/监听器编程模型,在这个模型中,有几个重要的角色:
-
事件:ApplicationEvent
-
应用事件监听器:ApplicationListener
-
事件发布者:ApplicationContext[容器上下文]
![](https://i-blog.csdnimg.cn/blog_migrate/707b6f0b51709327f05bca398a64202d.png)
2、场景实例
-
场景 一:等Spring容器初始化完成之后,再完成一些耗时的任务,例如 统计工作,数据加载,文件上传等;
-
场景二:系统检查,容器启动之后立马检查第三方的接口是否OK;
-
场景三:缓存预热;
-
场景四:检验数据;
-
场景五:关闭容器的时候,查看线程池是否执行完毕,勾子函数;
3、Spring消息机制
3.1、Spring事件:ApplicationEvent
Spring中提供的几种标准事件:
ContextRefreshEvent: 当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。此处的容器初始化指的所有的Bean都被成功装载,后处理器(post-processor)Bean被检测到并且被激活,所有的单例Bean都被预实例化,ApplicationContext容器已经可以使用。
ContextStartedEvent:
在spring容器启动后触发的事件,调用ConfigurableApplicationContext中的start()方法的时候执行。在经常需要停止后重新启动的场合比较适用。
ContextStoppedEvent: 当ApplicationContext容器停止的时候发布事件,即调用ConfigurableApplicationContext的close方法的时候。这里的停止是指,所有被容器管理生命周期的Bean接到一个
明确的停止信号。
ContextClosedEvent: 当Application关闭的时候发布事件,即调用ConfigurableApplicationContext的close()方法的时候,关闭指的是所有的
单例Bean都被销毁。关闭上下文后,不能重新刷新或者重新启动。
3.2、Spring容器消息事件实现
实践一:利用spring自带的容器消息机制完成一些功能,例如容器启动之后可以做一些耗时的操作:
@Component("userInfoService")
public class UserInfoService implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private CacheService userCache;
@Override //当一个ApplicationContext初始化完成时触发该操作
public void onApplicationEvent(ContextRefreshedEvent event) {
userCache.updateAllUseinfoList();//spring容器初始化完毕后全量更新用户列表信息;
System.out.println("spring容易初始化完毕===========================");
}
}
3.3、Spring自定义消息实现
事件发布者
ApplicationEvent,
容器事件源必须被ApplicationContext发布;
applicationContext.publishEvent(orderInfo);
事件监听者
ApplicationListener,
监听器,可由容器中任何监听器Bean担任。
-
实现方法一:实现ApplicationListener< T > 接口,自定义监听器,在onApplicationEvent(T t)中实现相应的监听功能;
-
实现方法二:使用@EventListener 注解,自定义消息handler处理;
注意:
需要在xml中配置相应的广播监听器的线程池,或者使用@EnableAsync异步注解,异步执行,否则是主线程完成任务,不会异步执行;
3.4、实践
模拟订单创建后发布一个订单消息,通知其他的服务进行其他增值业务的处理。
3.4.1、自定义消息的监听
方式一:消息的监听-实现接口
ApplicationListener<T>
@Component
public class GoodsOrderEventListener implements ApplicationListener<OrderInfo> {
@Resource
private AddGoodsService addGoodsService;
//自定义监听OrderInfo消息
public void onApplicationEvent(OrderInfo orderInfo) {
System.out.println("接收到订单信息,开始发送信息:");
addGoodsService.sendMsg();
}
}
方式二:使用Spring
的注解:@EventListener实现
//方式二:消息的监听-通过注解@EventListener
@EventListener
public void handlerOrderEvent(OrderInfo orderInfo) {
System.out.println("接受到订单消息,执行用户账单信息入库");
this.insertAccountInfo();
}
3.4.2、自定义消息的发布
@Service
public class OrderBiz implements ApplicationContextAware {
@Resource
private AddOrderService addOrderService;
@Resource
private ApplicationContext applicationContext;
//获取applicationContext 也是OK的;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContextTest = applicationContext;
}
//创建订单成功后发布一条订单消息;
public void createOrder(OrderInfo orderInfo){
System.out.println("开始下订单:");
if(addOrderService.addOrderInfo(orderInfo)){
//todo 事件发布者 发布自己的消息,开始执行增值业务
applicationContext.publishEvent(orderInfo);
} else {
System.out.println("订单创建失败!");
}
}
}
注意:如果是web容器,我们还需要判断一下根容器和子容器的类型,否则会导致该事件被执行两次
@Service
public class InitRunner implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
//root application context
if (contextRefreshedEvent.getApplicationContext().getParent() == null) { //根容器
/**
* 这里既是 root 根容器初始化完毕后,会进入该分支,
*/
//todo。。。
}else {//springMVC的容器,子容器,负责controller的扫描工作HandlerMapping
/**
* 如果是spring mvc的话, dispatchServlet对应的 applicationContext 会进入这个分支
*/
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
//todo。。。
}
}
}
4、SpringBoot中消息类型
类似的SpringBoot也提供了几种消息类型,
SpringBoot Application共支持6种事件监听,按顺序分别是:
-
1. ApplicationStartingEvent:在应用容器最开始启动的时候触发
-
2. ApplicationEnvironmentPreparedEvent:在容器已准备好上下文但是尚未创建的时候触发;
-
3. ApplicationPreparedEvent:在Bean定义加载之后、刷新上下文之前触发
-
4. ApplicationStartedEvent:在刷新上下文之后、容器启动之前触发
-
5. ApplicationReadyEvent:容器启动完成之后触发
-
6. ApplicationFailedEvent:在启动Spring发生异常时触发
SpringBoot的容器事件消息的发布/订阅实践
@Slf4j //容器启动完成后执行耗时的数据更新操作。
public class AppRunTask implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
ConfigurableApplicationContext applicationContext = applicationReadyEvent.getApplicationContext();
CacheService cacheService = applicationContext.getBean(CacheService.class);
userCache.updateAllUseinfoList();//更新用户列表信息;
}
}
5、小结
Spring的事件监听:为bean之间的消息通信提供支持。当一个bean做完一件事以后,通知另一个bean知晓并做出相应处理,很好的解决了操作和事务之间的通信问题,扩展灵活性强。
消息机制的通信思想在分布式中也起着举足轻重的作用,消息通信变的更加方便,提高了系统的吞吐量,让系统变的灵活,易扩展。
水滴石穿,积少成多。学习笔记,内容简单,用于复习,梳理巩固。