欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 业务解耦-Spring事件监听的三种实现方式

业务解耦-Spring事件监听的三种实现方式

2024/10/25 1:25:48 来源:https://blog.csdn.net/kusedexingfu/article/details/141723579  浏览:    关键词:业务解耦-Spring事件监听的三种实现方式

实现ApplicationListener

步骤如下:

1.写Event类,需要继承Spring的ApplicationEvent类

2.写监听类,需要实现Spring的ApplicationListener接口,加上@Component注解

3.监听类实现onApplicationEvent方法

4.通过ApplicationContext.publishEvent(Event)发布事件

Event的代码如下:

import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;@Getter
@Setter
public class CustomEvent extends ApplicationEvent  {private String message;public CustomEvent(Object source) {super(source);}}

两个Listener的代码如下。这里定义了两个Listener,@Order定义了执行顺序,其中CustomEventListener2先执行,CustomEventListener1后执行

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Order(2)
public class CustomEventListener1 implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {log.info ("CustomEventListener1 received: {}", JSON.toJSONString(event));}}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Order(1)
public class CustomEventListener2  implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {log.info ("CustomEventListener2 received: {}", JSON.toJSONString(event));}
}

发布事件的方法如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;@Service
public class SpringListenerService {@Autowiredprivate ApplicationContext applicationContext;public void customerListener() {CustomEvent event = new CustomEvent("CustomEventSource");event.setMessage("CustomEvent");applicationContext.publishEvent(event);}
}

启动项目调用SpringListenerService.customerListener方法后日志打印如下图,可以看到是按照Order定义的顺序执行的

需要注意的是:整个调用链都是同步执行的,如果某个Listener抛出了异常,那么后续的Listener也不会继续执行,而发布事件所在的方法也会受影响抛出异常。

如果某个Listener想要异步执行,可以在相应的onApplicationEvent方法上加上@Async注解(应用启动类上需要加上@EnableAsync注解来启用异步)

@EventListener

步骤如下:

1.写Event类,普通的POJO即可。

2.写监听类,加上@Component注解,即需要被Spring扫描到。

3.在监听类上写监听方法,需要加上@EventListener注解。方法的参数是Event类。

4.通过ApplicationContext.publishEvent(Event)发布事件,通过ApplicationEventPublisher发布事件也行

)。

Listener代码如下:定义了两个加@EventListener注解的方法,这两个方法都是监听的UserEvent,所以用@Order注解定义了顺序,数字越小优先级越高,越优先执行

import com.alibaba.fastjson2.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;@Component
@Slf4j
public class UserEventListener {//使用注解@EventListener//定义执行顺序@Order(2)public void receiveEvent2(UserEvent userEvent) {log.info("receiveEvent Order 2: {}", JSON.toJSONString(userEvent));}@EventListener@Order(1)public void receiveEvent(UserEvent userEvent) {log.info("receiveEvent Order 1 end: {}", JSON.toJSONString(userEvent));}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class UserEvent {private String name ;}
}

发布事件的方法如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;/*** @Description* @ClassName SpringListenerService* @Date 2024/8/29 23:01*/
@Service
public class SpringListenerService {@Autowiredprivate ApplicationContext applicationContext;public void eventListener() {UserEventListener.UserEvent eventListener = UserEventListener.UserEvent.builder().name("eventListener").build();applicationContext.publishEvent(eventListener);}
}

启动项目调用SpringListenerService.eventListener方法后日志打印如下图,可以看到是按照Order定义的顺序执行的

需要注意的是:整个调用链同样都是同步执行的,如果某个Listener抛出了异常,那么后续的Listener也不会继续执行,而发布事件所在的方法也会受影响抛出异常。

如果某个Listener想要异步执行,可以在相应的方法上加上@Async注解(应用启动类上需要加上@EnableAsync注解来启用异步)

@EventListener
//异步执行
@Async
@Order(1)
public void receiveEvent(UserEvent userEvent) {log.info("receiveEvent Order 1 end: {}", JSON.toJSONString(userEvent));
}

@TransactionalEventListener

以上介绍的两种实现方式,在处理某些场景的时候会有问题:如果Listener的代码异常不需要影响发布事件所在方法,就需要采用异步的方式。但是,如果发布事件所在的方法中存在事务,那么,Listener执行的时候,该事物可能还未提交,那么Listener中就会查不到相应的数据

这时候@TransactionalEventListener就能完美解决这个问题,它可以控制在事务的哪个阶段去执行监听。需要注意的是在Spring4.2+才有

先看下@TransactionalEventListener注解的内容:

/** Copyright 2002-2019 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.transaction.event;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AliasFor;/*** An {@link EventListener} that is invoked according to a {@link TransactionPhase}.** <p>If the event is not published within an active transaction, the event is discarded* unless the {@link #fallbackExecution} flag is explicitly set. If a transaction is* running, the event is processed according to its {@code TransactionPhase}.** <p>Adding {@link org.springframework.core.annotation.Order @Order} to your annotated* method allows you to prioritize that listener amongst other listeners running before* or after transaction completion.** @author Stephane Nicoll* @author Sam Brannen* @since 4.2*/
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {/*** Phase to bind the handling of an event to.* <p>The default phase is {@link TransactionPhase#AFTER_COMMIT}.* <p>If no transaction is in progress, the event is not processed at* all unless {@link #fallbackExecution} has been enabled explicitly.*/TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;/*** Whether the event should be processed if no transaction is running.*/boolean fallbackExecution() default false;/*** Alias for {@link #classes}.*/@AliasFor(annotation = EventListener.class, attribute = "classes")Class<?>[] value() default {};/*** The event classes that this listener handles.* <p>If 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 <em>not</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.* <p>The default is {@code ""}, meaning the event is always handled.* @see EventListener#condition*/String condition() default "";}

如果英语好的可以看源码的代码注释。

首先该注解添加了@EventListener注解,可见它是@EventListener的加强版

下面对一些重要的属性做解释。

phase:

这个注解取值有:BEFORE_COMMIT(事务提交前)、AFTER_COMMIT(事务提交后)、AFTER_ROLLBACK(事务回滚后)、AFTER_COMPLETION(事务完成时,无论是事务成功提交还是事务回滚)。默认值是BEFORE_COMMIT。

所以刚才提到的那种场景,在事务提交后才触发事件监听,我们可以用phase的默认属性AFTER_COMMIT即可。用该属性值,方法里有异常也不会影响发布事件所在的代码。

需要注意的是:BEFORE_COMMIT是在事务提交前执行的,所以如果出现了异常,也会影响发布事件所在的代码。如果BEFORE_COMMIT所指定的方法是异步执行的,那么可能出现在监听处查不到数据的情况,因为事务还可能未提交。

fallbackExecution:

用于指定:如果没有事务,是否执行相应的事务事件监听器。这个属性用处还是比较大的,如果在发布事件的位置没有事务,就可以指定该属性值为true该属性默认值为false,如果你发布的事件,监听不到,请仔细检查发布事件位置是否存在事务!!!!但是需要注意:如果指定为true,且发布事件的位置没有事务,监听的异常是会影响发布事件所在方法的代码的(此时等同于@EventListener)!!!!如果同时指定了异步,就不会影响!!!

代码使用方式跟@EventListener一致,只不过将注解换成了@TransactionalEventListener,这里不在做@TransactionalEventListener的代码展示

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com