场景
在某些业务场景中,往往需要异步事件来进行业务解耦、提高响应速度,如注册用户后发短信、同步信息等。这些场景可以使用MQ来解决,但有没有轻量一点的解决方案呢,有,Spring自带的事件监听配合异步方法就可以实现
Demo
-
开启异步事件
@EnableAsync -
新建事件,接收的对象可自定义,此处使用String演示
public class SysLogEvent extends ApplicationEvent {
/**
* @param source
*/
public SysLogEvent(String source) {
super(source);
}
}
- 监听器
@Slf4j
@Component
public class SysLogListener {
@Async
@EventListener(SysLogEvent.class)
public void saveSysLog(SysLogEvent event) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("【接收事件】{} {}", event.getSource(), event.getTimestamp());
}
}
- 发送监听事件
@RestController
@Slf4j
public class DemoController {
@Resource
private ApplicationContext ctx;
@GetMapping("/test")
public void test() {
log.info("【发送事件】");
ctx.publishEvent(new SysLogEvent("哈哈"));
}
}
完毕
异步线程池配置
@Async本质上是使用线程池进行处理任务,在实际开发中需要根据业务场景去配置线程池大小
/**
* @author HeyS1
* @description
*/
@Slf4j
@Configuration
@EnableAsync
public class SchedulingConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//核心线程
executor.setCorePoolSize(2);
//最大线程
executor.setMaxPoolSize(20);
//队列大小
executor.setQueueCapacity(5);
//表示线程池大小大于coreSize的时候,多余线程最多等待的时间,如果超过时间都没有处理会自行销毁
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("taskExecutor-");
//当pool已经达到max size的时候,如何处理新任务
//@see <a href="https://blog.csdn.net/foreverling/article/details/78073105"></a>
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
executor.initialize();
return executor;
}
/**
* 异步任务中异常处理
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (ex, method, params) -> {
log.error("==========================" + ex.getMessage() + "=======================", ex);
log.error("exception method:" + method.getName());
};
}
}
事件事务
使用@TransactionEventListener 代替 @EventListener
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EventListener
public @interface TransactionalEventListener {
//表示当前事件跟随消息发送方事务的出发时机,默认为消息发送方事务提交之后才进行处理。
TransactionPhase phase() default TransactionPhase.AFTER_COMMIT;
//true时不论发送方是否存在事务均出发当前事件处理逻辑
boolean fallbackExecution() default false;
//监听的事件具体类型,还是建议指定一下,避免监听到子类的一些情况出现
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] value() default {};
//指向@EventListener对应的值
@AliasFor(annotation = EventListener.class, attribute = "classes")
Class<?>[] classes() default {};
//指向@EventListener对应的值
String condition() default "";
}
public enum TransactionPhase {
// 指定目标方法在事务commit之前执行
BEFORE_COMMIT,
// 指定目标方法在事务commit之后执行
AFTER_COMMIT,
// 指定目标方法在事务rollback之后执行
AFTER_ROLLBACK,
// 指定目标方法在事务完成时执行,这里的完成是指无论事务是成功提交还是事务回滚了
AFTER_COMPLETION
}
补充
其实事件不继承ApplicationEvent 也是可以的
另外发送也可以使用
@Autowired
ApplicationEventPublisher publisher;
publisher.publishEvent(...);