什么是事件机制 ?
今天在工作中遇到一个问题,需要在原来运行的项目上,增加一功能:(每次访问项目都向redis中写入数据),但是不能影响原来项目的运行.具体实现的思路我用了两种:
1.直接在项目添加一个拦截器(也可以直接用@ModelAttribute注解,来完成此功能),在监听器里面做写数据的操作;
2.采用异步机制,类似于广播模式,在拦截器里面发布向redis写数据的信号,用spring的异步调用来进行向redis中写数据的实际操作;
为了在向redis中写数据的时候,如果出现了问题不影响原来的项目,所以采用了第2中,使用Spring的事件机制来解耦,利用观察者设计模式,设置监听器来监听访问事件.此时我们就可以把原有的拦截器理解成事件的发布者,一旦访问项目拦截器就发布向redis写数据的事件(向redis中写数据就可以理解成事件源,),监听器就会完成向redis中写数据的事件,但是是否执行成功并不会影响事件的发布者的运行.
具体的代码实现及解释
在这里面,我们涉及到三个主要对象:事件发布者、事件源、事件监听器。根据这三个对象,我们来配置我们的注册事件实例:
1.定义一个事件
在spring中,所有事件都必须扩展抽象类ApplicationEvent,同时将事件源作为构造函数参数,这样在发布的事件的时候才能把数据传输过来.
/**
* 定义事件
* 2018-08-09
*
*/
public class AccessEvent extends ApplicationEvent{
/**
* 定义发布事件时需要传输的数据作为成员变量
*/
private Request req;
public AccessEvent(Object source,Request req) {
super(source); // //source字面意思是根源,意指发送事件的根源,即我们的事件发布者
this.req = req;
}
public Request getReq() {
return req;
}
}
2.定义事件的发布者
事件发送的代表类是ApplicationEventPublisher,所以我们的事件发布者需要定义成员属性ApplicationEventPublisher来发布我们的事件.
/**
* 定义事件的发布者
*
*/
@Scope("prototype")
public class BaseController {
/**
* 注入ApplicationEventPublisher用来发布事件
*/
@Resource
private ApplicationEventPublisher publisher;
@ModelAttribute //被 @ModelAttribute 注解的方法会在Controller每个方法执行之前都执行
public void accessValidate(@RequestBody Request req, HttpServletRequest request, HttpServletResponse response) {
//发布任务,并传递事件源
publisher.publishEvent(new AccessEvent(this, req));
System.out.println("执行原有的程序" + new Date().toLocaleString());
}
}
3.定义事件的监听者
通过@EventListener监听事件,通过 @Async进行异步调用,(这里也可以只用其他方式,只是这样最简单.其他方式为:事件监听类需要实现我们的ApplicationListener接口,除了可以实现ApplicationListener定义事件监听器外,我们还可以让事件监听类实现SmartApplicationListener(智能监听器)接口,。关于它的具体用法和实现可参考我的下一篇文章《spring学习笔记(14)趣谈spring 事件机制[2]:多监听器流水线式顺序处理 》。而此外,如果我们事件监听器监听的事件类型唯一的话,我们可以通过泛型来简化配置。)
/**
* 定义事件的监听器
* @author zhouzp
*/
@Component
@EnableAsync
public class AccessEventListener {
@EventListener
@Async
public void addAccess(AccessEvent accessEvent){
Request req = accessEvent.getReq();
try {
Thread.sleep(1000*60);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("向redis中存数据"+new Date().toLocaleString());
}
}
测试结果及分析
从上面的截图的时间可以看到在异步事件休眠的过程并没有影响原来程序的执行
参考文档:
https://blog.csdn.net/m0_37779570/article/details/81102067
https://blog.csdn.net/qq_20261343/article/details/51071884