wordpress仿站容易被收录不,在线做任务的网站有哪些,php个人网站简洁,人才市场网站源码本文主要是简单的讲述了Spring的事件机制#xff0c;基本概念#xff0c;讲述了事件机制的三要素事件、事件发布、事件监听器。如何实现一个事件机制#xff0c;应用的场景#xff0c;搭配Async注解实现异步的操作等等。希望对大家有所帮助。
Spring的事件机制的基本概念 …本文主要是简单的讲述了Spring的事件机制基本概念讲述了事件机制的三要素事件、事件发布、事件监听器。如何实现一个事件机制应用的场景搭配Async注解实现异步的操作等等。希望对大家有所帮助。
Spring的事件机制的基本概念
Spring的事件机制是Spring框架中的一个重要特性基于观察者模式实现它可以实现应用程序中的解耦提高代码的可维护性和可扩展性。Spring的事件机制包括事件、事件发布、事件监听器等几个基本概念。其中事件是一个抽象的概念它代表着应用程序中的某个动作或状态的发生。事件发布是事件发生的地方它负责产生事件并通知事件监听器。事件监听器是事件的接收者它负责处理事件并执行相应的操作。在Spring的事件机制中事件源和事件监听器之间通过事件进行通信从而实现了模块之间的解耦。
举个例子用户修改密码修改完密码后需要短信通知用户记录关键性日志等等其他业务操作。
如下图就是我们需要调用多个服务来进行实现一个修改密码的功能。 使用了事件机制后我们只需要发布一个事件无需关心其扩展的逻辑让我们的事件监听器去处理从而实现了模块之间的解耦。 事件
通过继承ApplicationEvent实现自定义事件。是对 Java EventObject 的扩展表示 Spring 的事件Spring 中的所有事件都要基于其进行扩展。其源码如下。
我们可以获取到timestamp属性指的是发生时间。 事件发布
事件发布是事件发生的地方它负责产生事件并通知事件监听器。ApplicationEventPublisher用于用于发布 ApplicationEvent 事件发布后 ApplicationListener 才能监听到事件进行处理。源码如下。
需要一个ApplicationEvent就是我们的事件来进行发布事件。 事件监听器
ApplicationListener 是 Spring 事件的监听器用来接受事件所有的监听器都必须实现该接口。该接口源码如下。 Spring的事件机制的使用方法
下面会给大家演示如何去使用Spring的事件机制。就拿修改密码作为演示。
如何定义一个事件
新增一个类继承我们的ApplicationEvent。
如下面代码继承后定义了一个userId有一个UserChangePasswordEvent方法。这里就定义我们监听器需要的业务参数监听器需要那些参数我们这里就定义那些参数。
/*** Author JiaQIng* Description 修改密码事件* ClassName UserChangePasswordEvent* Date 2023/3/26 13:55**/
Getter
Setter
public class UserChangePasswordEvent extends ApplicationEvent {private String userId;public UserChangePasswordEvent(String userId) {super(new Object());this.userId userId;}
}
如何监听事件
实现监听器有两种方法
1、 新建一个类实现ApplicationListener接口并且重写onApplicationEvent方法注入到Spring容器中交给Spring管理如下代码新建了一个发送短信监听器收到事件后执行业务操作****
/*** Author JiaQIng* Description 发送短信监听器* ClassName MessageListener* Date 2023/3/26 14:16**/
Component
public class MessageListener implements ApplicationListenerUserChangePasswordEvent {Overridepublic void onApplicationEvent(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作给用户发送短信。用户userId为 event.getUserId());}
}
1、 使用EventListener注解标注处理事件的方法此时Spring将创建一个ApplicationListenerbean对象使用给定的方法处理事件源码如下参数可以给指定的事件这里巧妙的用到了AliasFor的能力放到了EventListener身上注意一般建议都需要指定此值否则默认可以处理所有类型的事件范围太广了 代码如下。新建一个事件监听器注入到Spring容器中交给Spring管理。在指定方法上添加EventListener参数为监听的事件。方法为业务代码。使用 EventListener 注解的好处是一个类可以写很多监听器定向监听不同的事件或者同一个事件。
/*** Author JiaQIng* Description 事件监听器* ClassName LogListener* Date 2023/3/26 14:22**/
Component
public class ListenerEvent {EventListener({ UserChangePasswordEvent.class })public void LogListener(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作生成关键日志。用户userId为 event.getUserId());}EventListener({ UserChangePasswordEvent.class })public void messageListener(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作给用户发送短信。用户userId为 event.getUserId());}
}
1、 TransactionalEventListener来定义一个监听器他与EventListener不同的就是EventListener标记一个方法作为监听器他默认是同步执行如果发布事件的方法处于事务中那么事务会在监听器方法执行完毕之后才提交事件发布之后就由监听器去处理而不要影响原有的事务也就是说希望事务及时提交我们就可以使用该注解来标识注意此注解需要spring-tx的依赖
注解源码如下主要是看一下注释内容。
// 在这个注解上面有一个注解EventListener所以表明其实这个注解也是个事件监听器。
Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
EventListener
public interface TransactionalEventListener {/*** 这个注解取值有BEFORE_COMMIT(指定目标方法在事务commit之前执行)、AFTER_COMMIT(指定目标方法在事务commit之后执行)、* AFTER_ROLLBACK(指定目标方法在事务rollback之后执行)、AFTER_COMPLETION(指定目标方法在事务完成时执行这里的完成是指无论事务是成功提交还是事务回滚了)* 各个值都代表什么意思表达什么功能非常清晰* 需要注意的是AFTER_COMMIT AFTER_COMPLETION是可以同时生效的* AFTER_ROLLBACK AFTER_COMPLETION是可以同时生效的*/TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;/*** 表明若没有事务的时候对应的event是否需要执行默认值为false表示没事务就不执行了。*/boolean fallbackExecution() default false;/*** 这里巧妙的用到了AliasFor的能力放到了EventListener身上* 注意一般建议都需要指定此值否则默认可以处理所有类型的事件范围太广了。*/AliasFor(annotation EventListener.class, attribute classes)Class?[] value() default {};/*** The event classes that this listener handles.* pIf this attribute is specified with a single value, the annotated* method may optionally accept a single parameter. However, if this* attribute is specified with multiple values, the annotated method* must emnot/em declare any parameters.*/AliasFor(annotation EventListener.class, attribute classes)Class?[] classes() default {};/*** Spring Expression Language (SpEL) attribute used for making the event* handling conditional.* pThe default is {code }, meaning the event is always handled.* see EventListener#condition*/AliasFor(annotation EventListener.class, attribute condition)String condition() default ;/*** An optional identifier for the listener, defaulting to the fully-qualified* signature of the declaring method (e.g. mypackage.MyClass.myMethod()).* since 5.3* see EventListener#id* see TransactionalApplicationListener#getListenerId()*/AliasFor(annotation EventListener.class, attribute id)String id() default ;}
使用方式如下。phase事务类型value指定事件。
/*** Author JiaQIng* Description 事件监听器* ClassName LogListener* Date 2023/3/26 14:22**/
Component
public class ListenerEvent {EventListener({ UserChangePasswordEvent.class })public void logListener(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作生成关键日志。用户userId为 event.getUserId());}TransactionalEventListener(phase TransactionPhase.AFTER_COMMIT,value { UserChangePasswordEvent.class })public void messageListener(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作给用户发送短信。用户userId为 event.getUserId());}
}
如何发布一个事件
1、 使用ApplicationContext进行发布由于ApplicationContext已经继承了ApplicationEventPublisher因此可以直接使用发布事件源码如下 1、 直接注入我们的ApplicationEventPublisher使用Autowired注入一下
三种发布事件的方法我给大家演示一下Autowired注入的方式发布我们的事件。
SpringBootTest
class SpirngEventApplicationTests {AutowiredApplicationEventPublisher appEventPublisher;Testvoid contextLoads() {appEventPublisher.publishEvent(new UserChangePasswordEvent(1111111));}}
我们执行一下看一下接口。 测试成功。
搭配Async注解实现异步操作
监听器默认是同步执行的如果我们想实现异步执行可以搭配Async注解使用但是前提条件是你真的懂Async注解使用不当会出现问题的。 后续我会出一篇有关Async注解使用的文章。这里就不给大家详细的解释了。有想了解的同学可以去网上学习一下有关Async注解使用。
使用Async时需要配置线程池否则用的还是默认的线程池也就是主线程池线程池使用不当会浪费资源严重的会出现OOM事故。
下图是阿里巴巴开发手册的强制要求。 简单的演示一下这里声明一下俺没有使用线程池只是简单的演示一下。
1、 在我们的启动类上添加EnableAsync开启异步执行配置
EnableAsync
SpringBootApplication
public class SpirngEventApplication {public static void main(String[] args) {SpringApplication.run(SpirngEventApplication.class, args);}}
1、 在我们想要异步执行的监听器上添加Async注解
/*** Author JiaQIng* Description 事件监听器* ClassName LogListener* Date 2023/3/26 14:22**/
Component
public class ListenerEvent {AsyncEventListener({ UserChangePasswordEvent.class })public void logListener(UserChangePasswordEvent event) {System.out.println(收到事件: event);System.out.println(开始执行业务操作生成关键日志。用户userId为 event.getUserId());}
}
这样我们的异步执行监听器的业务操作就完成了。
Spring的事件机制的应用场景
1、 告警操作比喻钉钉告警异常告警可以通过事件机制进行解耦2、 关键性日志记录和业务埋点比喻说我们的关键日志需要入库记录一下操作时间操作人变更内容等等可以通过事件机制进行解耦3、 性能监控比喻说一些接口的时长性能方便的埋点等可以通过事件机制进行解耦4、 .......一切与主业务无关的操作都可以通过这种方式进行解耦常用的场景大概就上述提到的而且很多架构的源码都有使用这种机制如GateWaySpring等等
Spring的事件机制的注意事项
1、 对于同一个事件有多个监听器的时候注意可以通过Order注解指定顺序Order的value值越小执行的优先级就越高2、 如果发布事件的方法处于事务中那么事务会在监听器方法执行完毕之后才提交事件发布之后就由监听器去处理而不要影响原有的事务也就是说希望事务及时提交我们就可以TransactionalEventListener来定义一个监听器3、 监听器默认是同步执行的如果我们想实现异步执行可以搭配Async注解使用但是前提条件是你真的懂Async注解使用不当会出现问题的4、 对于同一个事件有多个监听器的时候如果出现了异常后续的监听器就失效了因为他是把同一个事件的监听器add在一个集合里面循环执行如果出现异常需要注意捕获异常处理异常