Spring观察者模式
1、ApplicationContext事件机制是观察者设计模式的具体实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
2、如果容器中由一个ApplicationListener Bean,当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean会自动触发,这种事件机制需要程序显示的触发,
3、其中Spring有一些内置的事件,当完成某种操作会发出某些事件动作,例如监听ContextRefreshedEvent事件,当所有Bean促使和完成并被成功装载会触发该事件,实现ApplicationListener<‘ContextRefreshedEvent’>接口可以收到监听动作,接着实现自己的业务逻辑,事件Event也可以自己自定义、监听Listener也可以自定义,根据业务逻辑按需处理。
文章目录
一、对象说明
1、ApplicationContext容器对象
2、ApplicationEvent事件对象
3、ContextRefreshedEvent容器刷新事件
4、ApplicationListener事件监听对象
二、事件模式实体概念
1.事件源:事件的触发的地方,比如我在容器加载的时候需要触发加载数据字典实现缓存预热。
2.事件:描述发生了什么事情的对象,比如上面的:加载数据字典数据
3.事件监听器:监听到事件发生的时候,做一些处理,比如更新缓存实现缓存预热
三、本文场景:容器启动的时候装填缓存数据(加载数据字典)
某个零售电商系统,mysql数据库中有储存系统内设置好的所有省市区信息,现在要将这部分地址数据查询出来放到redis做缓存(缓存预热),方便用户购买的时候自动级联填写快递省市区地址,为了避免直接写到监听的时候对象还未加载完毕,导致缓存预热失败,打算从ContextRefreshedEvent等容器加载完毕的时候进行缓存预热,可以进行以下处理:
1.创建监听器CountryDirectoryEventListener
CountryDirectoryEventListener必须实现ApplicationListener类并指定监听事件ContextRefreshedEvent,如果不指明监听类型则会监听所有事件,因为所有事件继承ApplicationContextEvent类
代码如下(示例):
@Component
public class CountryDirectoryEventListener implements ApplicationListener<ContextRefreshedEvent> {
/**
* 监听发生指定事件后执行
* @param event
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
System.out.println("---查询数据库省市区数据---");
System.out.println("---填充到redis缓存---");
System.out.println("---数据预热结束---");
}
}
2.运行截图
这里只是打印输出,具体实现思路大致如此
MyApplicationEventListener类没有指定具体的监听器,则所有事件都会监听到
思考问题:事件的发布与监听是一个同步的过程是同步的?
是的,事件的发布与监听是一个同步的过程,一般缓存预热这里可以使用异步的方法实现,通过在监听方法前使用@Async,并且工程要支持异步,springboot工程启动类需要加@EnableAsync注解。下面看看实际效果
1.创建自定义事件类:MessageEvent
必须继承ApplicationEvent,可以支持多参数传递
@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
@Override
public void onApplicationEvent(MessageEvent event) {
for (int i = 0 ; i < 10 ; i++){
System.out.println("---等待一秒钟---" + System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("监听到消息事件");
}
}
2.创建自定义事件监听类:MessageEventListener
@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
@Override
public void onApplicationEvent(MessageEvent event) {
System.out.println("监听到消息事件");
}
}
这里偷懒不写接口调用方法,直接在刚刚缓存预热的监听方法之前去发布事件MessageEvent看看会不会是同步的。
@Component
public class CountryDirectoryEventListener implements ApplicationListener<ContextRefreshedEvent> {
/**
* 监听发生指定事件后执行
* @param event
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
applicationContext.publishEvent(new MessageEvent("我来发消息了"));
System.out.println("---查询数据库省市区数据---");
System.out.println("---填充到redis缓存---");
System.out.println("---数据预热结束---");
}
}
3.运行效果
事件的发布与监听是一个同步的过程
4.异步执行效果
使用@Async注解
@Component
public class MessageEventListener implements ApplicationListener<MessageEvent> {
@Async
@Override
public void onApplicationEvent(MessageEvent event) {
for (int i = 0 ; i < 10 ; i++){
System.out.println("---等待一秒钟---" + System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("监听到消息事件");
}
}
//启动类
@SpringBootApplication
@EnableAsync
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
高级写法@EventListener注解
@Component
public class CommentSyncEventListener {
@Async
@EventListener
public void sync(CommentSyncEvent event) {
logger.error("评论同步失败", e);
}
}
总结
Spring的事件机制简便地将我们的代码解耦从而优化我们的代码,方便我们扩展。