谈到Spring 事件驱动模型,我想你们都不陌生,事件驱动模型,一般也能够说是观察者设计模式,对观察者设计模式不熟悉的朋友能够看我以前写的笔记,设计模式java语言实现之观察者模式,在java事件驱动的支持中,EventBus作移动端开发的朋友应该都比较了解,其实,java自己也自带了对事件驱动的支持,可是大部分都是用于咱们的客户端开发,好比GUI ,Swing这些,而Spring 则在java的基础上,扩展了对事件驱动的支持。java
不说废话,直接上代码git
首先,咱们新建一个类NotifyEvent 继承ApplicationEvent,用于封装咱们事件额外的信息,这里则是String类型的msg,用于记录详细的事件内容。github
public class NotifyEvent extends ApplicationEvent {
private String msg;
public NotifyEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
public String getMsg() {
return msg;
}
}
复制代码
其中,ApplicationEvent 是一个抽象类,扩展了java自己的EventObject 类,每个继承了ApplicationEvent的子类都表示一类事件,能够携带数据。web
而后新建一个NotifyPublisher用于咱们事件的发布工做,该类实现了ApplicationContextAware并重写了setApplicationContext 方法,这一步的目的是能够获取咱们Spring的应用上下文,由于事件的发布是须要应用上下文来作的,不了解应用上下文的同窗能够去看个人另一篇笔记:到底什么是上下文?编程
@Component //声明成组件,为了后期注入方便
public class NotifyPublisher implements ApplicationContextAware {
private ApplicationContext ctx; //应用上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx= applicationContext;
}
// 发布一个消息,这里你们能够根据不一样的状态实现发布不一样的事件,我这里就只写了一个事件类,因此if else
//都发布NotifyEvent事件。
public void publishEvent(int status, String msg) {
if (status == 0) {
ctx.publishEvent(new NotifyEvent(this, msg));
} else {
ctx.publishEvent(new NotifyEvent(this,msg)) ;
}
}
}
复制代码
最后一步就是实现一个类做为事件的订阅者啦,当事件发布时,会通知订阅者,而后订阅者作相关的处理,好比新用户注册发送事件自动发送欢迎邮件等等。同时,Spring 4.2 版本更新的EventListener,能够很方便帮助咱们实现事件与方法的绑定,只须要在目标方法上加上EventListener便可。设计模式
@Component
public class NotifyListener {
@EventListener
//参数NotifyEvent ,当有NotifyEvent 类型的事件发生时,交给sayHello方法处理
public void sayHello(NotifyEvent notifyEvent){
System.out.println("收到事件:"+notifyEvent.getMsg());
}
}
复制代码
**测试:**编写咱们的测试类TestController。浏览器
@RestController
public class TestController {
@Autowired
private NotifyPublisher notifyPublisher;
@GetMapping("/sayHello")
public String sayHello(){
notifyPublisher.publishEvent(1, "我发布了一个事件");
return "Hello Word";
}
}
复制代码
启动咱们的应用,在浏览器中输入http://127.0.0.1:8080/sayHello,控制台输出:app
2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
收到事件:我发布了一个事件
复制代码
划个知识点:异步
若是一个新的事件继承了NotifyEvent,当咱们推送NotifyEvent类型的事件时,NotifyEvent和其子类的监听器均可以收到该事件。ide
完了吗,尚未,平常除了听到过事件驱动编程,偶尔还会见到异步事件驱动编程这几个字,一样的Spring 也提供了@Async 注解来实现异步事件的消费。用起来也很简单,只须要在 @EventListener上加上@Async 就行了。
代码以下:
@Component
public class NotifyListener {
@Async
@EventListener
public void sayHello(NotifyEvent notifyEvent){
System.out.println("收到事件:"+notifyEvent.getMsg());
}
}
复制代码
最后配置一个线程池
@Configuration
@EnableAsync
public class AysncListenerConfig implements AsyncConfigurer {
/** * 获取异步线程池执行对象 * * @return */
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.initialize();
executor.setCorePoolSize(10); //核心线程数
executor.setMaxPoolSize(20); //最大线程数
executor.setQueueCapacity(1000); //队列大小
executor.setKeepAliveSeconds(300); //线程最大空闲时间
executor.setThreadNamePrefix("ics-Executor-"); ////指定用于新建立的线程名称的前缀。
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
return new ExceptionHandlingAsyncTaskExecutor(executor);
}
}
复制代码
public class ExceptionHandlingAsyncTaskExecutor implements AsyncTaskExecutor {
private AsyncTaskExecutor executor;
public ExceptionHandlingAsyncTaskExecutor(AsyncTaskExecutor executor) {
this.executor = executor;
}
//用独立的线程来包装,@Async其本质就是如此
public void execute(Runnable task) {
executor.execute(createWrappedRunnable(task));
}
public void execute(Runnable task, long startTimeout) {
//用独立的线程来包装,@Async其本质就是如此
executor.execute(createWrappedRunnable(task), startTimeout);
}
public Future submit(Runnable task) { return executor.submit(createWrappedRunnable(task));
//用独立的线程来包装,@Async其本质就是如此。
}
public Future submit(final Callable task) {
//用独立的线程来包装,@Async其本质就是如此。
return executor.submit(createCallable(task));
}
private Callable createCallable(final Callable task) {
return new Callable(){
@Override
public Object call() throws Exception {
try {
return task.call();
} catch (Exception ex) {
handle(ex);
throw ex;
}
}
};
}
private Runnable createWrappedRunnable(final Runnable task) {
return new Runnable() {
public void run() {
try {
task.run();
} catch (Exception ex) {
handle(ex);
}
}
};
}
private void handle(Exception ex) {
//具体的异常逻辑处理的地方
System.err.println("Error during @Async execution: " + ex);
}
}
复制代码
测试:编写咱们的测试类TestController。
2019-09-28 16:55:51.902 INFO 716 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 12 ms
收到事件:我发布了一个事件
复制代码
大功告成啦。
电子版笔记和代码已经开源至github(欢迎star哦):