1、Event应用场景
在实际业务开发过程中,业务逻辑可能非常复杂,有一些业务场景不需要在一次请求中同步执行,比如邮件发送、短信发送等。针对这类业务,如何优雅的将这类业务进行解耦,可能我们第一想法就是使用RocketMQ进行解耦,MQ确实可以解决这个问题,但MQ重啊,非必要不提升架构复杂度。针对这样的业务,Spring Event是一个不错的解决方案。
Spring Event其实就是一个观察者设计模式,即一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。
2、Spring Event实战
启动类:
@SpringBootApplication
public class ElasticsearchApplication {
public static void main(String[] args) {
SpringApplication.run(ElasticsearchApplication.class,args);
}
}
配置文件:
server:
port: 8091
定义首次登录事件实体:
public class LogonEvent<T> extends ApplicationEvent {
/**
* 消费者实体
*/
private T t;
public LogonEvent(Object source, T t) {
super(source);
this.t = t;
}
public LogonEvent(@NonNull Object source) {
super(source);
}
public T getConsumer() {
return t;
}
public void setConsumer(T t) {
this.t = t;
}
}
定义事件发布器:
@Component
public class LogonEventPublisher implements ApplicationEventPublisherAware, ApplicationContextAware {
/**
* 日志管理组件
*/
private static final Logger LOGGER = LoggerFactory.getLogger(LogonEventPublisher.class);
/**
* 上下文容器
*/
private ApplicationContext applicationContext;
/**
* 事件发布组件
*/
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void setApplicationEventPublisher(@NonNull ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/**
* 广播📢{@link LogonEventPublisher}事件
*
* @param t 消费者
*/
public <T> void publishEvent(T t) {
LOGGER.info("消费者事件发布开始,参数:{}", JSON.toJSONString(t));
LogonEvent event = new LogonEvent(applicationContext);
event.setConsumer(t);
applicationEventPublisher.publishEvent(event);
LOGGER.info("事件发布结束");
LOGGER.info("broadcast FirstLogonEvent");
}
}
定义事件发布者:
@Slf4j
@RestController
@RequestMapping("/listenerEvent")
public class ListenerEventController {
@Autowired
private LogonEventPublisher logonEventPublisher;
@GetMapping("/publisher")
public ResponseMessage<Void> publisher(){
log.info("其它业务逻辑开始执行.......");
log.info("监听事件开始执行.......");
Consumer consumer = new Consumer();
consumer.setConsumerName("张三");
consumer.setSex("男");
logonEventPublisher.publishEvent(consumer);
log.info("监听事件Controller结束执行.......");
return new OverAllExceptionAdvice<Void>().sendSuccessResponse();
}
}
2.1、Event同步使用
定义事件监听器-同步执行: 监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解。
@Slf4j
@Service
public class LogonListener {
/**
* 异步执行,async注解
* 登录事件监听器
*/
@EventListener(LogonEvent.class)
public void dispatch(LogonEvent event) {
log.info("监听到登录事件event:{}", event);
// 模拟异步场景
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Consumer consumer = (Consumer)event.getConsumer();
log.info("监听到的事件内容时:{}",consumer);
log.info("登录事件监听完成");
}
}
执行结果:
2.2、Event异步使用
修改启动类,添加开启异步注解@EnableAsync
@EnableAsync
@SpringBootApplication
public class ElasticsearchApplication {
public static void main(String[] args) {
SpringApplication.run(ElasticsearchApplication.class,args);
}
}
定义事件监听器-异步执行:
在监听方法上添加注解@Async
@Slf4j
@Service
public class LogonListener {
/**
* 异步执行,async注解
* 登录事件监听器
*/
@EventListener(LogonEvent.class)
@Async
public void dispatch(LogonEvent event) {
log.info("监听到登录事件event:{}", event);
// 模拟异步场景
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Consumer consumer = (Consumer)event.getConsumer();
log.info("监听到的事件内容时:{}",consumer);
log.info("登录事件监听完成");
}
}
执行结果: