大家好,我是编辑君!
  • 前言
  • ApplicationEvent 与 ApplicationListener 应用
    • 实现
    • 基于注解
    • 事件过滤
    • 异步事件监听
  • 好处及应用场景
  • 源码阅读
  • 总结

1前言

ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现了 ApplicationListener 接口的 bean 部署到容器中,则每次将 ApplicationEvent 发布到ApplicationContext 时,都会通知到该 bean,这简直是典型的观察者模式。设计的初衷就是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。
Spring 中提供了以下的事件

2ApplicationEvent 与 ApplicationListener 应用

实现
1、自定义事件类,基于 ApplicationEvent 实现扩展
publicclassDemoEventextendsApplicationEvent
{

privatestaticfinallong
 serialVersionUID = -
2753705718295396328L
;

private
 String msg;


publicDemoEvent(Object source, String msg)
{

super
(source);

this
.msg = msg;

    }


public String getMsg()
{

return
 msg;

    }


publicvoidsetMsg(String msg)
{

this
.msg = msg;

    }

}

2、定义 Listener 类,实现 ApplicationListener接口,并且注入到 IOC 中。等发布者发布事件时,都会通知到这个bean,从而达到监听的效果。
@Component
publicclassDemoListenerimplementsApplicationListener<DemoEvent
{

@Override
publicvoidonApplicationEvent(DemoEvent demoEvent)
{

        String msg = demoEvent.getMsg();

        System.out.println(
"bean-listener 收到了 publisher 发布的消息: "
 + msg);

    }

}

3、要发布上述自定义的 event,需要调用 ApplicationEventPublisher 的 publishEvent 方法,我们可以定义一个实现 ApplicationEventPublisherAware 的类,并注入 IOC来进行调用。
@Component
publicclassDemoPublisherimplementsApplicationEventPublisherAware
{


private
 ApplicationEventPublisher applicationEventPublisher;


@Override
publicvoidsetApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
{

this
.applicationEventPublisher = applicationEventPublisher;

    }


publicvoidsendMsg(String msg)
{

        applicationEventPublisher.publishEvent(
new
 DemoEvent(
this
, msg));

    }

}

4、客户端调用 publisher
@RestController
@RequestMapping
(
"/event"
)

publicclassDemoClientimplementsApplicationContextAware
{

private
 ApplicationContext applicationContext;


@Override
publicvoidsetApplicationContext(ApplicationContext applicationContext)throws BeansException 
{

this
.applicationContext = applicationContext;

    }


@GetMapping
(
"/publish"
)

publicvoidpublish()
{

        DemoPublisher bean = applicationContext.getBean(DemoPublisher
.class)
;

        bean.sendMsg(
"发布者发送消息......"
);

    }

}

输出结果:
bean-listener 收到了 publisher 发布的消息: 发布者发送消息......
基于注解
我们可以不用实现 AppplicationListener 接口 ,在方法上使用 @EventListener 注册事件。如果你的方法应该侦听多个事件,并不使用任何参数来定义,可以在 @EventListener 注解上指定多个事件。
重写 DemoListener 类如下:
publicclassDemoListener
{

@EventListener
(value = {DemoEvent
.
class
TestEvent
.
class
})

publicvoidprocessApplicationEvent
(
DemoEventevent
{

        String msg = event.getMsg();

        System.out.println(
"bean-listener 收到了 publisher 发布的消息: "
 + msg);

    }

}

事件过滤
如果希望通过一定的条件对事件进行过滤,可以使用 @EventListener 的 condition 属性。以下实例中只有 event 的 msg 属性是 my-event 时才会进行调用。
@EventListener
(value = {DemoEvent
.classTestEvent.class}, condition
"#event.msg == 'my-event'"
)

publicvoidprocessApplicationEvent(DemoEvent event)
{

     String msg = event.getMsg();

     System.out.println(
"bean-listener 收到了 publisher 发布的消息: "
 + msg);

 }

此时,发送符合条件的消息,listener 才会侦听到 publisher 发布的消息。
bean-listener 收到了 publisher 发布的消息: my-event
异步事件监听
前面提到的都是同步处理事件,那如果我们希望某个特定的侦听器异步去处理事件,如何做?
使用 @Async 注解可以实现类内方法的异步调用,这样方法在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
@EventListener
@Async
publicvoidprocessApplicationEvent(DemoEvent event)
{

    String msg = event.getMsg();

    System.out.println(
"bean-listener 收到了 publisher 发布的消息: "
 + msg);

}

使用异步监听时,有两点需要注意:
  • 如果异步事件抛出异常,则不会将其传播到调用方。
  • 异步事件监听方法无法通过返回值来发布后续事件,如果需要作为处理结果发布另一个事件,请插入 ApplicationEventPublisher 以手动发布事件

3好处及应用场景

ApplicationContext 在运行期会自动检测到所有实现了 ApplicationListener 的 bean,并将其作为事件接收对象。当我们与 spring 上下文交互触发 publishEvent 方法时,每个实现了 ApplicationListener 的 bean 都会收到 ApplicationEvent 对象,每个 ApplicationListener 可以根据需要只接收自己感兴趣的事件。
这样做有什么好处呢?
在传统的项目中,各个业务逻辑之间耦合性比较强,controller 和 service 间都是关联关系,然而,使用 ApplicationEvent 监听 publisher 这种方式,类间关系是什么样的?我们不如画张图来看看。
DemoPublisherDemoListener 两个类间并没有直接关联,解除了传统业务逻辑两个类间的关联关系,将耦合降到最小。这样在后期更新、维护时难度大大降低了。
ApplicationEvent 使用观察者模式实现,那什么时候适合使用观察者模式呢?观察者模式也叫 发布-订阅模式,例如,微博的订阅,我们订阅了某些微博账号,当这些账号发布消息时,我们都会收到通知。
总结来说,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,从而实现广播的效果。

4源码阅读

Spring中的事件机制流程
1、ApplicationEventPublisher是Spring的事件发布接口,事件源通过该接口的pulishEvent方法发布事件
2、ApplicationEventMulticaster就是Spring事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster实现,如果用户没有自定义广播器,则使用默认的。它通过父类AbstractApplicationEventMulticastergetApplicationListeners方法从事件注册表(事件-监听器关系保存)中获取事件监听器,并且通过invokeListener方法执行监听器的具体逻辑
3、ApplicationListener就是Spring的事件监听器接口,所有的监听器都实现该接口,本图中列出了典型的几个子类。其中RestartApplicationListnener在SpringBoot的启动框架中就有使用
4、在Spring中通常是ApplicationContext本身担任监听器注册表的角色,在其子类AbstractApplicationContext中就聚合了事件广播器ApplicationEventMulticaster和事件监听器ApplicationListnener,并且提供注册监听器的addApplicationListnener方法
ChatGPT中文网站:https://ai.cxyquan.com/   
通过上图就能较清晰的知道当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher发布事件,然后事件广播器ApplicationEventMulticaster会去事件注册表ApplicationContext中找到事件监听器ApplicationListnener,并且逐个执行监听器的onApplicationEvent方法,从而完成事件监听器的逻辑。
来到ApplicationEventPublisher 的 publishEvent 方法内部
protectedvoidpublishEvent(Object event, @Nullable ResolvableType eventType)
{

if
 (
this
.earlyApplicationEvents != 
null
) {

this
.earlyApplicationEvents.add(applicationEvent);

 }

else
 {

// 
  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

 }

}

多播事件方法
@Override
publicvoidmulticastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType)
{

 ResolvableType type = (eventType != 
null
 ? eventType : resolveDefaultEventType(event));

 Executor executor = getTaskExecutor();

// 遍历所有的监听者
for
 (ApplicationListener<?> listener : getApplicationListeners(event, type)) {

if
 (executor != 
null
) {

// 异步调用监听器
   executor.execute(() -> invokeListener(listener, event));

  }

else
 {

// 同步调用监听器
   invokeListener(listener, event);

  }

 }

}

invokeListener
protectedvoidinvokeListener(ApplicationListener<?> listener, ApplicationEvent event)
{

 ErrorHandler errorHandler = getErrorHandler();

if
 (errorHandler != 
null
) {

try
 {

   doInvokeListener(listener, event);

  }

catch
 (Throwable err) {

   errorHandler.handleError(err);

  }

 }

else
 {

  doInvokeListener(listener, event);

 }

}

doInvokeListener
privatevoiddoInvokeListener(ApplicationListener listener, ApplicationEvent event)
{

try
 {

// 这里是事件发生的地方
  listener.onApplicationEvent(event);

 }

catch
 (ClassCastException ex) {

  ......

 }

}

点击 ApplicationListener 接口 onApplicationEvent 方法的实现,可以看到我们重写的方法。

5总结

Spring 使用反射机制,获取了所有继承 ApplicationListener 接口的监听器,在 Spring 初始化时,会把监听器都自动注册到注册表中。
Spring 的事件发布非常简单,我们来总结一下:
  • 定义一个继承 ApplicationEvent 的事件
  • 定义一个实现 ApplicationListener 的监听器或者使用 @EventListener 监听事件
  • 定义一个发送者,调用 ApplicationContext 直接发布或者使用 ApplicationEventPublisher 来发布自定义事件
最后,发布-订阅模式可以很好的将业务逻辑进行解耦(上图验证过),大大提高了可维护性、可扩展性。
来源:blog.csdn.net/weixin_42653522/
article/details/117151913
End
-END-
PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。
 关注公众号:Java后端编程,回复下面关键字 
要Java学习完整路线,回复  路线 
缺Java入门视频,回复 视频 
要Java面试经验,回复  面试 
缺Java项目,回复: 项目 
进Java粉丝群: 加群 
PS:如果觉得我的分享不错,欢迎大家随手点赞、在看。
(完)
加我"微信获取一份 最新Java面试题资料
请备注:666不然不通过~
最近好文
最近面试BAT,整理一份面试资料Java面试BAT通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:关注公众号并回复 java 领取,更多内容陆续奉上。
明天见(。・ω・。)
继续阅读
阅读原文