Spring发布事件publishEvent 发布-订阅(观察者模式)
一、应用场景
我们一般是在不同的bean之间进行信息传递,比如我们beanA的事件处理完后,需要beanB进行处理一些业务逻辑的时候这种情况就一般可以使用publishEvent解决。(可以用作日志处理)
二、实现原理
ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口来提供的,通过ApplicationContext的publishEvent()方法发布到ApplicationListener。
一个事件模型有三个组成部分:被监听对象source(也称为事件源),事件event和监听对象listener。事件发布者在发布事件的时候->通知事件的监听者。
三、事件触发和监听处理过程
(1) 使用 org.springframework.context 包下的 ApplicationContext.publishEvent(ApplicationEvent appEvent) 发布事件。
(2) 使用 org.springframework.context.event 包下的 @EventListener(事件名) 监听事件并处理。
注意:
(1) ApplicationContext.publishEvent 默认是同步操作, 并非发布后不管的异步操作,发布事件后需要等 @EventListener 执行完。
(2) 如果需要开启异步操作需要在@EventListener上增加@Async 注解。
四、简单代码实现(记录操作日志)
1、自定义注解
/**
* 自定义操作日志记录注解
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
/**
* 模块
*/
public String title() default "";
/**
* 功能
*/
public String content() default "";
}
2、切面类
/**
* 操作日志记录处理
*/
@Aspect
@Slf4j
@Component
public class OperLogAspect {
@Autowired
private ApplicationContext applicationContext;
// 配置织入点
@Pointcut("@annotation(com.lkl.spring.log.annotation.OperLog)")
public void logPointCut() {
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "logPointCut()")
public void doAfterReturning(JoinPoint joinPoint) {
handleLog(joinPoint, null);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "logPointCut()", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
handleLog(joinPoint, e);
}
protected void handleLog(final JoinPoint joinPoint, final Exception e) {
try {
// 获得注解
OperLog controllerLog = getAnnotationLog(joinPoint);
if (controllerLog == null) {
return;
}
// *========数据库日志=========*//
SysOperLog operLog = new SysOperLog();
operLog.setTitle(controllerLog.title());
operLog.setContent(controllerLog.content());
if (e != null) {
//日志异常内容记录
}
// 发布事件
applicationContext.publishEvent(new SysOperLogEvent(operLog));
} catch (Exception exp) {
// 记录本地异常日志
log.error("==前置通知异常==");
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
}
}
/**
* 是否存在注解,如果存在就获取
*/
private OperLog getAnnotationLog(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(OperLog.class);
}
return null;
}
}
3、自定义事件(继承ApplicationEvent类)
/**
* 系统日志事件
*/
public class SysOperLogEvent extends ApplicationEvent
{
private static final long serialVersionUID = 8905017895058642111L;
public SysOperLogEvent(SysOperLog source)
{
super(source);
}
}
4.1、自定义监听器(注解方式 @EventListener(监听某个类)),@Async开启异步处理
/**
* 异步监听日志事件 注解方式
*/
@Slf4j
@AllArgsConstructor
@Component
public class LogListener {
@Async
@Order
@EventListener(SysOperLogEvent.class)
public void listenOperLog(SysOperLogEvent event) {
SysOperLog sysOperLog = (SysOperLog) event.getSource();
System.out.println("数据库插入操作");
log.info("远程操作日志记录成功:{}", sysOperLog);
}
}
主启动类开启异步操作 @EnableAsync
@EnableAsync
@SpringBootApplication
public class SpringApplyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringApplyApplication.class, args);
}
}
测试如下:
验证成功
4.2、自定义监听器(实现 ApplicationListener接口)
/**
* 实现 ApplicationListener 接口
*/
@Slf4j
@Component
public class LogListenerOther implements ApplicationListener<SysOperLogEvent> {
@Override
public void onApplicationEvent(SysOperLogEvent event) {
SysOperLog sysOperLog = (SysOperLog) event.getSource();
System.out.println("数据库插入操作222");
log.info("远程操作日志记录成功222:{}", sysOperLog);
}
}
验证成功