Event-业务解耦的神器-大幅优化可裁减性-Spring (event翻译)
一、前言
licationContext中的事情处置是经过ApplicationEvent类和ApplicationListener接口提供的。假设将成功了ApplicationListener接口的bean部署到容器中,则每次将ApplicationEvent颁布到ApplicationContext时,都会通知到该bean,这简直是典型的观察者形式。设计的初衷就是为了系统业务逻辑之间的解耦,提高可裁减性以及可保养性。
Spring中提供了以下的事情:
二、ApplicationEvent与ApplicationListener运行
1.成功
自定义事情类,基于ApplicationEvent成功裁减:
publicclassDemoEventextendsApplicationEvent{privatestaticfinallongserialVersionUID=-2753705718295396328L;privateStringmsg;publicDemoEvent(Objectsource,Stringmsg){super(source);this.msg=msg;}publicStringgetMsg(){returnmsg;}publicvoidsetMsg(Stringmsg){this.msg=msg;}}
定义Listener类,成功ApplicationListener接口,并且注入到IOC中。等颁布者颁布事情时,都会通知到这个bean,从而到达监听的成果。
@ComponentpublicclassDemoListenerimplementsApplicationListener<DemoEvent>{@OverridepublicvoidonApplicationEvent(DemoEventdemoEvent){Stringmsg=demoEvent.getMsg();System.out.println("bean-listener收到了publisher颁布的信息:"+msg);}}
要颁布上述自定义的event,须要调用ApplicationEventPublisher的publishEvent方法,咱们可以定义一个成功ApplicationEventPublisherAware的类,并注入IOC来启动调用:
@ComponentpublicclassDemoPublisherimplementsApplicationEventPublisherAware{privateApplicationEventPublisherapplicationEventPublisher;@OverridepublicvoidsetApplicationEventPublisher(ApplicationEventPublisherapplicationEventPublisher){this.applicationEventPublisher=applicationEventPublisher;}publicvoidsendMsg(Stringmsg){applicationEventPublisher.publishEvent(newDemoEvent(this,msg));}}
客户端调用publisher:
@RestController@RequestMapping("/event")publicclassDemoClientimplementsApplicationContextAware{privateApplicationContextapplicationContext;@OverridepublicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{this.applicationContext=applicationContext;}@GetMapping("/publish")publicvoidpublish(){DemoPublisherbean=applicationContext.getBean(DemoPublisher.class);bean.sendMsg("颁布者发送信息......");}}
输入结果:
bean-listener收到了publisher颁布的信息:颁布者发送信息......
2.基于注解
咱们可以不用成功AppplicationListener接口,在方法上经常使用@EventListener注册事情。假设你的方法应该侦听多个事情,并不经常使用任何参数来定义,可以在@EventListener注解上指定多个事情。
重写DemoListener类如下:
publicclassDemoListener{@EventListener(value={DemoEvent.class,TestEvent.class})publicvoidprocessApplicationEvent(DemoEventevent){Stringmsg=event.getMsg();System.out.println("bean-listener收到了publisher颁布的信息:"+msg);}}
3.事情过滤
假设宿愿经过必定的条件对事情启动过滤,可以经常使用@EventListener的condition属性。以下实例中只要event的msg属性是my-event时才会启动调用。
@EventListener(value={DemoEvent.class,TestEvent.class},condition="#event.msg=='my-event'")publicvoidprocessApplicationEvent(DemoEventevent){Stringmsg=event.getMsg();System.out.println("bean-listener收到了publisher颁布的信息:"+msg);}
此时,发送合乎条件的信息,listener才会侦听到publisher颁布的信息。
bean-listener收到了publisher颁布的信息:my-event
4.异步事情监听
前面提到的都是同步处置事情,那假设咱们宿愿某个特定的侦听器异步去处置事情,如何做?
经常使用@Async注解可以成功类内方法的异步伐用,这样方法在执行的时刻,将会在独立的线程中被执行,调用者无需期待它的成功,即可继续其余的操作。
@EventListener@AsyncpublicvoidprocessApplicationEvent(DemoEventevent){Stringmsg=event.getMsg();System.out.println("bean-listener收到了publisher颁布的信息:"+msg);}
经常使用异步监听时,有两点须要留意:
三、好处及运行场景
ApplicationContext在运转期会智能检测到一切成功了ApplicationListener的bean,并将其作为事情接纳对象。当咱们与spring高低文交互触发publishEvent方法时,每个成功了ApplicationListener的bean都会收到ApplicationEvent对象,每个ApplicationListener可以依据须要只接纳自己感兴味的事情。
这样做有什么好处呢?
在传统的名目中,各个业务逻辑之间耦合性比拟强,controller和service间都是关联相关,但是,经常使用ApplicationEvent监听publisher这种形式,类间相关是什么样的?咱们不如画张图来看看。
DemoPublisher和DemoListener两个类间并没有间接关联,解除了传统业务逻辑两个类间的关联相关,将耦合降到最小。这样在前期降级、保养时难度大大降落了。
ApplicationEvent经常使用观察者形式成功,那什么时刻适宜经常使用观察者形式呢?观察者形式也叫颁布-订阅形式,例如,微博的订阅,咱们订阅了某些微博账号,当这些账号颁布信息时,咱们都会收到通知。
总结来说,定义对象间的一种一对多的依赖相关,当一个对象的形态出现扭转时,一切依赖于它的对象都失掉通知并被智能降级,从而成功广播的成果。
四、源码浏览
Spring中的事情机制流程:
经过上图就能较明晰的知道当一个事情源发生事情时,它经过事情颁布器ApplicationEventPublisher颁布事情,而后事情广播器ApplicationEventMulticaster会去事情注册表ApplicationContext中找到事情监听器ApplicationListnener,并且一一执行监听器的onApplicationEvent方法,从而成功事情监听器的逻辑。
到来ApplicationEventPublisher的publishEvent方法外部:
protectedvoidpublishEvent(Objectevent,@NullableResolvableTypeeventType){if(this.earlyApplicationEvents!=null){this.earlyApplicationEvents.add(applicationEvent);}else{//getApplicationEventMulticaster().multicastEvent(applicationEvent,eventType);}}
多播事情方法:
@OverridepublicvoidmulticastEvent(finalApplicationEventevent,@NullableResolvableTypeeventType){ResolvableTypetype=(eventType!=null?eventType:resolveDefaultEventType(event));Executorexecutor=getTaskExecutor();//遍历一切的监听者for(ApplicationListener<?>listener:getApplicationListeners(event,type)){if(executor!=null){//异步伐用监听器executor.execute(()->invokeListener(listener,event));}else{//同步伐用监听器invokeListener(listener,event);}}}
invokeListener:
protectedvoidinvokeListener(ApplicationListener<?>listener,ApplicationEventevent){ErrorHandlererrorHandler=getErrorHandler();if(errorHandler!=null){try{doInvokeListener(listener,event);}catch(Throwableerr){errorHandler.handleError(err);}}else{doInvokeListener(listener,event);}}
doInvokeListener:
privatevoiddoInvokeListener(ApplicationListenerlistener,ApplicationEventevent){try{//这里是事情出现的中央listener.onApplicationEvent(event);}catch(ClassCastExceptionex){......}}
点击ApplicationListener接口onApplicationEvent方法的成功,可以看到咱们重写的方法。
五、总结
Spring经常使用反射机制,失掉了一切承袭ApplicationListener接口的监听器,在Spring初始化时,会把监听器都智能注册到注册表中。
Spring的事情颁布十分便捷,咱们来总结一下:
最后,颁布-订阅形式可以很好的将业务逻辑启动解耦(上图验证过),大大提高了可保养性、可裁减性。
springevent可以跨服务监听。 根据查询相关信息springevent监听范围1、定义事件ApplicationEvent2、定义监听ApplicationListener接口,要么在方法上添加EventListener注解。 3、发布事件,调用()或者()。 比如用户注册成功以后,系统要给用户发送一封邮件,同时还要给用户发放优惠券,为了跟注册流程解耦,可以在注册成功以后发出一个事件,让其他服务来监听。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。